Skip to content

Commit d6389af

Browse files
committed
Remove lower priority from contextual signature instantiation return type inference
1 parent 10b784a commit d6389af

10 files changed

+263
-29
lines changed

src/compiler/checker.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33860,7 +33860,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3386033860
});
3386133861
if (!inferenceContext) {
3386233862
applyToReturnTypes(contextualSignature, signature, (source, target) => {
33863-
inferTypes(context.inferences, source, target, InferencePriority.ReturnType);
33863+
inferTypes(context.inferences, source, target);
3386433864
});
3386533865
}
3386633866
return getSignatureInstantiation(signature, getInferredTypes(context), isInJSFile(contextualSignature.declaration));

tests/baselines/reference/assignmentCompatWithCallSignatures3.errors.txt

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -50,19 +50,14 @@ assignmentCompatWithCallSignatures3.ts(80,1): error TS2322: Type '(x: Base[], y:
5050
assignmentCompatWithCallSignatures3.ts(83,1): error TS2322: Type '(x: Base[], y: Derived[]) => Derived[]' is not assignable to type '<T extends Derived[]>(x: Base[], y: T) => T'.
5151
Type 'Derived[]' is not assignable to type 'T'.
5252
'Derived[]' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'Derived[]'.
53-
assignmentCompatWithCallSignatures3.ts(85,1): error TS2322: Type '<T>(x: { a: T; b: T; }) => T' is not assignable to type '(x: { a: string; b: number; }) => Object'.
54-
Types of parameters 'x' and 'x' are incompatible.
55-
Type '{ a: string; b: number; }' is not assignable to type '{ a: string; b: string; }'.
56-
Types of property 'b' are incompatible.
57-
Type 'number' is not assignable to type 'string'.
5853
assignmentCompatWithCallSignatures3.ts(86,1): error TS2322: Type '(x: { a: string; b: number; }) => Object' is not assignable to type '<T>(x: { a: T; b: T; }) => T'.
5954
Types of parameters 'x' and 'x' are incompatible.
6055
Type '{ a: T; b: T; }' is not assignable to type '{ a: string; b: number; }'.
6156
Types of property 'a' are incompatible.
6257
Type 'T' is not assignable to type 'string'.
6358

6459

65-
==== assignmentCompatWithCallSignatures3.ts (15 errors) ====
60+
==== assignmentCompatWithCallSignatures3.ts (14 errors) ====
6661
// these are all permitted with the current rules, since we do not do contextual signature instantiation
6762

6863
class Base { foo: string; }
@@ -218,12 +213,6 @@ assignmentCompatWithCallSignatures3.ts(86,1): error TS2322: Type '(x: { a: strin
218213
!!! error TS2322: 'Derived[]' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'Derived[]'.
219214
var b14: <T>(x: { a: T; b: T }) => T;
220215
a14 = b14; // ok
221-
~~~
222-
!!! error TS2322: Type '<T>(x: { a: T; b: T; }) => T' is not assignable to type '(x: { a: string; b: number; }) => Object'.
223-
!!! error TS2322: Types of parameters 'x' and 'x' are incompatible.
224-
!!! error TS2322: Type '{ a: string; b: number; }' is not assignable to type '{ a: string; b: string; }'.
225-
!!! error TS2322: Types of property 'b' are incompatible.
226-
!!! error TS2322: Type 'number' is not assignable to type 'string'.
227216
b14 = a14; // ok
228217
~~~
229218
!!! error TS2322: Type '(x: { a: string; b: number; }) => Object' is not assignable to type '<T>(x: { a: T; b: T; }) => T'.

tests/baselines/reference/assignmentCompatWithConstructSignatures3.errors.txt

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -50,19 +50,14 @@ assignmentCompatWithConstructSignatures3.ts(80,1): error TS2322: Type 'new (x: B
5050
assignmentCompatWithConstructSignatures3.ts(83,1): error TS2322: Type 'new (x: Base[], y: Derived[]) => Derived[]' is not assignable to type 'new <T extends Derived[]>(x: Base[], y: T) => T'.
5151
Type 'Derived[]' is not assignable to type 'T'.
5252
'Derived[]' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'Derived[]'.
53-
assignmentCompatWithConstructSignatures3.ts(85,1): error TS2322: Type 'new <T>(x: { a: T; b: T; }) => T' is not assignable to type 'new (x: { a: string; b: number; }) => Object'.
54-
Types of parameters 'x' and 'x' are incompatible.
55-
Type '{ a: string; b: number; }' is not assignable to type '{ a: string; b: string; }'.
56-
Types of property 'b' are incompatible.
57-
Type 'number' is not assignable to type 'string'.
5853
assignmentCompatWithConstructSignatures3.ts(86,1): error TS2322: Type 'new (x: { a: string; b: number; }) => Object' is not assignable to type 'new <T>(x: { a: T; b: T; }) => T'.
5954
Types of parameters 'x' and 'x' are incompatible.
6055
Type '{ a: T; b: T; }' is not assignable to type '{ a: string; b: number; }'.
6156
Types of property 'a' are incompatible.
6257
Type 'T' is not assignable to type 'string'.
6358

6459

65-
==== assignmentCompatWithConstructSignatures3.ts (15 errors) ====
60+
==== assignmentCompatWithConstructSignatures3.ts (14 errors) ====
6661
// checking assignment compatibility relations for function types. All of these are valid.
6762

6863
class Base { foo: string; }
@@ -218,12 +213,6 @@ assignmentCompatWithConstructSignatures3.ts(86,1): error TS2322: Type 'new (x: {
218213
!!! error TS2322: 'Derived[]' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'Derived[]'.
219214
var b14: new <T>(x: { a: T; b: T }) => T;
220215
a14 = b14; // ok
221-
~~~
222-
!!! error TS2322: Type 'new <T>(x: { a: T; b: T; }) => T' is not assignable to type 'new (x: { a: string; b: number; }) => Object'.
223-
!!! error TS2322: Types of parameters 'x' and 'x' are incompatible.
224-
!!! error TS2322: Type '{ a: string; b: number; }' is not assignable to type '{ a: string; b: string; }'.
225-
!!! error TS2322: Types of property 'b' are incompatible.
226-
!!! error TS2322: Type 'number' is not assignable to type 'string'.
227216
b14 = a14; // ok
228217
~~~
229218
!!! error TS2322: Type 'new (x: { a: string; b: number; }) => Object' is not assignable to type 'new <T>(x: { a: T; b: T; }) => T'.
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
callbackAssignabilityErrorMessage.ts(31,1): error TS2322: Type 'Source1' is not assignable to type 'Target'.
2+
The types returned by 'map(...)' are incompatible between these types.
3+
Type '(T | undefined)[]' is not assignable to type 'T[]'.
4+
Type 'T | undefined' is not assignable to type 'T'.
5+
'T' could be instantiated with an arbitrary type which could be unrelated to 'T | undefined'.
6+
7+
8+
==== callbackAssignabilityErrorMessage.ts (1 errors) ====
9+
export interface Source1 {
10+
map: <S>(callbackfn: (value: string) => S) => S[];
11+
}
12+
13+
export interface Target {
14+
map: <T>(callbackfn: (value: string) => T | undefined) => T[];
15+
}
16+
17+
declare let s1: Source1;
18+
declare let t: Target;
19+
20+
// During the following assignment, `Source1["map"]` gets
21+
// instantiated with the contextual type `Target["map"]` before checking
22+
// assignability. To do that, an inference is made to `S`. For `Source1`,
23+
// the only candidate is `T | undefined` from the return type of `callbackfn`.
24+
// Inference also runs on `map` return types `S[]` and `T[]`, but the result
25+
// is discarded because it’s run with a lower inference priority. As a result,
26+
// we end up seeing if the contextually instantiated source signature:
27+
//
28+
// (callbackfn: (value: string) => T | undefined) => (T | undefined)[]
29+
//
30+
// is assignable to the target signature:
31+
//
32+
// (callbackfn: (value: string) => T | undefined) => T[]
33+
//
34+
// and the return types cause the failed assignability. But as a human reader
35+
// interpreting why `s1` is not assignable to `t`, I instead equate `S` and `T`
36+
// and identify the “real” problem as the return type of `callbackfn` in `Target`
37+
// (`T | undefined`) being a supertype of the one in `Source1` (`S` → `T`).
38+
39+
t = s1; // Bad: instantiates `S` with `T | undefined`, fails `map` return type assignability
40+
~
41+
!!! error TS2322: Type 'Source1' is not assignable to type 'Target'.
42+
!!! error TS2322: The types returned by 'map(...)' are incompatible between these types.
43+
!!! error TS2322: Type '(T | undefined)[]' is not assignable to type 'T[]'.
44+
!!! error TS2322: Type 'T | undefined' is not assignable to type 'T'.
45+
!!! error TS2322: 'T' could be instantiated with an arbitrary type which could be unrelated to 'T | undefined'.
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//// [tests/cases/compiler/callbackAssignabilityErrorMessage.ts] ////
2+
3+
//// [callbackAssignabilityErrorMessage.ts]
4+
export interface Source1 {
5+
map: <S>(callbackfn: (value: string) => S) => S[];
6+
}
7+
8+
export interface Target {
9+
map: <T>(callbackfn: (value: string) => T | undefined) => T[];
10+
}
11+
12+
declare let s1: Source1;
13+
declare let t: Target;
14+
15+
// During the following assignment, `Source1["map"]` gets
16+
// instantiated with the contextual type `Target["map"]` before checking
17+
// assignability. To do that, an inference is made to `S`. For `Source1`,
18+
// the only candidate is `T | undefined` from the return type of `callbackfn`.
19+
// Inference also runs on `map` return types `S[]` and `T[]`, but the result
20+
// is discarded because it’s run with a lower inference priority. As a result,
21+
// we end up seeing if the contextually instantiated source signature:
22+
//
23+
// (callbackfn: (value: string) => T | undefined) => (T | undefined)[]
24+
//
25+
// is assignable to the target signature:
26+
//
27+
// (callbackfn: (value: string) => T | undefined) => T[]
28+
//
29+
// and the return types cause the failed assignability. But as a human reader
30+
// interpreting why `s1` is not assignable to `t`, I instead equate `S` and `T`
31+
// and identify the “real” problem as the return type of `callbackfn` in `Target`
32+
// (`T | undefined`) being a supertype of the one in `Source1` (`S` → `T`).
33+
34+
t = s1; // Bad: instantiates `S` with `T | undefined`, fails `map` return type assignability
35+
36+
//// [callbackAssignabilityErrorMessage.js]
37+
// During the following assignment, `Source1["map"]` gets
38+
// instantiated with the contextual type `Target["map"]` before checking
39+
// assignability. To do that, an inference is made to `S`. For `Source1`,
40+
// the only candidate is `T | undefined` from the return type of `callbackfn`.
41+
// Inference also runs on `map` return types `S[]` and `T[]`, but the result
42+
// is discarded because it’s run with a lower inference priority. As a result,
43+
// we end up seeing if the contextually instantiated source signature:
44+
//
45+
// (callbackfn: (value: string) => T | undefined) => (T | undefined)[]
46+
//
47+
// is assignable to the target signature:
48+
//
49+
// (callbackfn: (value: string) => T | undefined) => T[]
50+
//
51+
// and the return types cause the failed assignability. But as a human reader
52+
// interpreting why `s1` is not assignable to `t`, I instead equate `S` and `T`
53+
// and identify the “real” problem as the return type of `callbackfn` in `Target`
54+
// (`T | undefined`) being a supertype of the one in `Source1` (`S` → `T`).
55+
t = s1; // Bad: instantiates `S` with `T | undefined`, fails `map` return type assignability
56+
export {};
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//// [tests/cases/compiler/callbackAssignabilityErrorMessage.ts] ////
2+
3+
=== callbackAssignabilityErrorMessage.ts ===
4+
export interface Source1 {
5+
>Source1 : Symbol(Source1, Decl(callbackAssignabilityErrorMessage.ts, 0, 0))
6+
7+
map: <S>(callbackfn: (value: string) => S) => S[];
8+
>map : Symbol(Source1.map, Decl(callbackAssignabilityErrorMessage.ts, 0, 26))
9+
>S : Symbol(S, Decl(callbackAssignabilityErrorMessage.ts, 1, 8))
10+
>callbackfn : Symbol(callbackfn, Decl(callbackAssignabilityErrorMessage.ts, 1, 11))
11+
>value : Symbol(value, Decl(callbackAssignabilityErrorMessage.ts, 1, 24))
12+
>S : Symbol(S, Decl(callbackAssignabilityErrorMessage.ts, 1, 8))
13+
>S : Symbol(S, Decl(callbackAssignabilityErrorMessage.ts, 1, 8))
14+
}
15+
16+
export interface Target {
17+
>Target : Symbol(Target, Decl(callbackAssignabilityErrorMessage.ts, 2, 1))
18+
19+
map: <T>(callbackfn: (value: string) => T | undefined) => T[];
20+
>map : Symbol(Target.map, Decl(callbackAssignabilityErrorMessage.ts, 4, 25))
21+
>T : Symbol(T, Decl(callbackAssignabilityErrorMessage.ts, 5, 8))
22+
>callbackfn : Symbol(callbackfn, Decl(callbackAssignabilityErrorMessage.ts, 5, 11))
23+
>value : Symbol(value, Decl(callbackAssignabilityErrorMessage.ts, 5, 24))
24+
>T : Symbol(T, Decl(callbackAssignabilityErrorMessage.ts, 5, 8))
25+
>T : Symbol(T, Decl(callbackAssignabilityErrorMessage.ts, 5, 8))
26+
}
27+
28+
declare let s1: Source1;
29+
>s1 : Symbol(s1, Decl(callbackAssignabilityErrorMessage.ts, 8, 11))
30+
>Source1 : Symbol(Source1, Decl(callbackAssignabilityErrorMessage.ts, 0, 0))
31+
32+
declare let t: Target;
33+
>t : Symbol(t, Decl(callbackAssignabilityErrorMessage.ts, 9, 11))
34+
>Target : Symbol(Target, Decl(callbackAssignabilityErrorMessage.ts, 2, 1))
35+
36+
// During the following assignment, `Source1["map"]` gets
37+
// instantiated with the contextual type `Target["map"]` before checking
38+
// assignability. To do that, an inference is made to `S`. For `Source1`,
39+
// the only candidate is `T | undefined` from the return type of `callbackfn`.
40+
// Inference also runs on `map` return types `S[]` and `T[]`, but the result
41+
// is discarded because it’s run with a lower inference priority. As a result,
42+
// we end up seeing if the contextually instantiated source signature:
43+
//
44+
// (callbackfn: (value: string) => T | undefined) => (T | undefined)[]
45+
//
46+
// is assignable to the target signature:
47+
//
48+
// (callbackfn: (value: string) => T | undefined) => T[]
49+
//
50+
// and the return types cause the failed assignability. But as a human reader
51+
// interpreting why `s1` is not assignable to `t`, I instead equate `S` and `T`
52+
// and identify the “real” problem as the return type of `callbackfn` in `Target`
53+
// (`T | undefined`) being a supertype of the one in `Source1` (`S` → `T`).
54+
55+
t = s1; // Bad: instantiates `S` with `T | undefined`, fails `map` return type assignability
56+
>t : Symbol(t, Decl(callbackAssignabilityErrorMessage.ts, 9, 11))
57+
>s1 : Symbol(s1, Decl(callbackAssignabilityErrorMessage.ts, 8, 11))
58+
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//// [tests/cases/compiler/callbackAssignabilityErrorMessage.ts] ////
2+
3+
=== callbackAssignabilityErrorMessage.ts ===
4+
export interface Source1 {
5+
map: <S>(callbackfn: (value: string) => S) => S[];
6+
>map : <S>(callbackfn: (value: string) => S) => S[]
7+
> : ^ ^^ ^^ ^^^^^
8+
>callbackfn : (value: string) => S
9+
> : ^ ^^ ^^^^^
10+
>value : string
11+
> : ^^^^^^
12+
}
13+
14+
export interface Target {
15+
map: <T>(callbackfn: (value: string) => T | undefined) => T[];
16+
>map : <T>(callbackfn: (value: string) => T | undefined) => T[]
17+
> : ^ ^^ ^^ ^^^^^
18+
>callbackfn : (value: string) => T | undefined
19+
> : ^ ^^ ^^^^^
20+
>value : string
21+
> : ^^^^^^
22+
}
23+
24+
declare let s1: Source1;
25+
>s1 : Source1
26+
> : ^^^^^^^
27+
28+
declare let t: Target;
29+
>t : Target
30+
> : ^^^^^^
31+
32+
// During the following assignment, `Source1["map"]` gets
33+
// instantiated with the contextual type `Target["map"]` before checking
34+
// assignability. To do that, an inference is made to `S`. For `Source1`,
35+
// the only candidate is `T | undefined` from the return type of `callbackfn`.
36+
// Inference also runs on `map` return types `S[]` and `T[]`, but the result
37+
// is discarded because it’s run with a lower inference priority. As a result,
38+
// we end up seeing if the contextually instantiated source signature:
39+
//
40+
// (callbackfn: (value: string) => T | undefined) => (T | undefined)[]
41+
//
42+
// is assignable to the target signature:
43+
//
44+
// (callbackfn: (value: string) => T | undefined) => T[]
45+
//
46+
// and the return types cause the failed assignability. But as a human reader
47+
// interpreting why `s1` is not assignable to `t`, I instead equate `S` and `T`
48+
// and identify the “real” problem as the return type of `callbackfn` in `Target`
49+
// (`T | undefined`) being a supertype of the one in `Source1` (`S` → `T`).
50+
51+
t = s1; // Bad: instantiates `S` with `T | undefined`, fails `map` return type assignability
52+
>t = s1 : Source1
53+
> : ^^^^^^^
54+
>t : Target
55+
> : ^^^^^^
56+
>s1 : Source1
57+
> : ^^^^^^^
58+

tests/baselines/reference/subtypingWithCallSignatures2.types

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1178,8 +1178,10 @@ var r14arg2 = (x: { a: string; b: number }) => <Object>null;
11781178
> : ^^^^^^
11791179

11801180
var r14 = foo14(r14arg1); // any
1181-
>r14 : any
1182-
>foo14(r14arg1) : any
1181+
>r14 : (x: { a: string; b: number; }) => Object
1182+
> : ^ ^^ ^^^^^^^^^^^
1183+
>foo14(r14arg1) : (x: { a: string; b: number; }) => Object
1184+
> : ^ ^^ ^^^^^^^^^^^
11831185
>foo14 : { (a: (x: { a: string; b: number; }) => Object): (x: { a: string; b: number; }) => Object; (a: any): any; }
11841186
> : ^^^ ^^ ^^^^ ^^ ^^^^^^^^^^^^^^ ^^ ^^^^^^^^^
11851187
>r14arg1 : <T>(x: { a: T; b: T; }) => T

tests/baselines/reference/subtypingWithConstructSignatures2.types

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,8 +1038,10 @@ var r14arg2: new (x: { a: string; b: number }) => Object;
10381038
> : ^^^^^^
10391039

10401040
var r14 = foo14(r14arg1); // any
1041-
>r14 : any
1042-
>foo14(r14arg1) : any
1041+
>r14 : new (x: { a: string; b: number; }) => Object
1042+
> : ^^^^^ ^^ ^^^^^^^^^^^
1043+
>foo14(r14arg1) : new (x: { a: string; b: number; }) => Object
1044+
> : ^^^^^ ^^ ^^^^^^^^^^^
10431045
>foo14 : { (a: new (x: { a: string; b: number; }) => Object): new (x: { a: string; b: number; }) => Object; (a: any): any; }
10441046
> : ^^^ ^^ ^^^^^^^^ ^^ ^^^^^^^^^^^^^^ ^^ ^^^^^^^^^
10451047
>r14arg1 : new <T>(x: { a: T; b: T; }) => T
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// @strict: true
2+
// @target: esnext
3+
4+
5+
export interface Source1 {
6+
map: <S>(callbackfn: (value: string) => S) => S[];
7+
}
8+
9+
export interface Target {
10+
map: <T>(callbackfn: (value: string) => T | undefined) => T[];
11+
}
12+
13+
declare let s1: Source1;
14+
declare let t: Target;
15+
16+
// During the following assignment, `Source1["map"]` gets
17+
// instantiated with the contextual type `Target["map"]` before checking
18+
// assignability. To do that, an inference is made to `S`. For `Source1`,
19+
// the only candidate is `T | undefined` from the return type of `callbackfn`.
20+
// Inference also runs on `map` return types `S[]` and `T[]`, but the result
21+
// is discarded because it’s run with a lower inference priority. As a result,
22+
// we end up seeing if the contextually instantiated source signature:
23+
//
24+
// (callbackfn: (value: string) => T | undefined) => (T | undefined)[]
25+
//
26+
// is assignable to the target signature:
27+
//
28+
// (callbackfn: (value: string) => T | undefined) => T[]
29+
//
30+
// and the return types cause the failed assignability. But as a human reader
31+
// interpreting why `s1` is not assignable to `t`, I instead equate `S` and `T`
32+
// and identify the “real” problem as the return type of `callbackfn` in `Target`
33+
// (`T | undefined`) being a supertype of the one in `Source1` (`S` → `T`).
34+
35+
t = s1; // Bad: instantiates `S` with `T | undefined`, fails `map` return type assignability

0 commit comments

Comments
 (0)