Skip to content

Commit af940f1

Browse files
authored
Merge pull request #19424 from Microsoft/inferenceFromGenericFunction
Improve inference from generic functions
2 parents ceba507 + 414f165 commit af940f1

17 files changed

+291
-79
lines changed

src/compiler/checker.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6726,6 +6726,16 @@ namespace ts {
67266726
isInJavaScriptFile(signature.declaration));
67276727
}
67286728

6729+
function getBaseSignature(signature: Signature) {
6730+
const typeParameters = signature.typeParameters;
6731+
if (typeParameters) {
6732+
const typeEraser = createTypeEraser(typeParameters);
6733+
const baseConstraints = map(typeParameters, tp => instantiateType(getBaseConstraintOfType(tp), typeEraser) || emptyObjectType);
6734+
return instantiateSignature(signature, createTypeMapper(typeParameters, baseConstraints), /*eraseTypeParameters*/ true);
6735+
}
6736+
return signature;
6737+
}
6738+
67296739
function getOrCreateTypeFromSignature(signature: Signature): ObjectType {
67306740
// There are two ways to declare a construct signature, one is by declaring a class constructor
67316741
// using the constructor keyword, and the other is declaring a bare construct signature in an
@@ -10955,7 +10965,7 @@ namespace ts {
1095510965
const targetLen = targetSignatures.length;
1095610966
const len = sourceLen < targetLen ? sourceLen : targetLen;
1095710967
for (let i = 0; i < len; i++) {
10958-
inferFromSignature(getErasedSignature(sourceSignatures[sourceLen - len + i]), getErasedSignature(targetSignatures[targetLen - len + i]));
10968+
inferFromSignature(getBaseSignature(sourceSignatures[sourceLen - len + i]), getBaseSignature(targetSignatures[targetLen - len + i]));
1095910969
}
1096010970
}
1096110971

tests/baselines/reference/genericCallWithConstructorTypedArguments5.errors.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithConstructorTypedArguments5.ts(11,14): error TS2345: Argument of type '{ cb: new <T>(x: T, y: T) => string; }' is not assignable to parameter of type '{ cb: new (t: any) => string; }'.
1+
tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithConstructorTypedArguments5.ts(11,14): error TS2345: Argument of type '{ cb: new <T>(x: T, y: T) => string; }' is not assignable to parameter of type '{ cb: new (t: {}) => string; }'.
22
Types of property 'cb' are incompatible.
3-
Type 'new <T>(x: T, y: T) => string' is not assignable to type 'new (t: any) => string'.
3+
Type 'new <T>(x: T, y: T) => string' is not assignable to type 'new (t: {}) => string'.
44
tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithConstructorTypedArguments5.ts(13,14): error TS2345: Argument of type '{ cb: new (x: string, y: number) => string; }' is not assignable to parameter of type '{ cb: new (t: string) => string; }'.
55
Types of property 'cb' are incompatible.
66
Type 'new (x: string, y: number) => string' is not assignable to type 'new (t: string) => string'.
@@ -19,9 +19,9 @@ tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithCon
1919
var arg2: { cb: new <T>(x: T, y: T) => string };
2020
var r2 = foo(arg2); // error
2121
~~~~
22-
!!! error TS2345: Argument of type '{ cb: new <T>(x: T, y: T) => string; }' is not assignable to parameter of type '{ cb: new (t: any) => string; }'.
22+
!!! error TS2345: Argument of type '{ cb: new <T>(x: T, y: T) => string; }' is not assignable to parameter of type '{ cb: new (t: {}) => string; }'.
2323
!!! error TS2345: Types of property 'cb' are incompatible.
24-
!!! error TS2345: Type 'new <T>(x: T, y: T) => string' is not assignable to type 'new (t: any) => string'.
24+
!!! error TS2345: Type 'new <T>(x: T, y: T) => string' is not assignable to type 'new (t: {}) => string'.
2525
var arg3: { cb: new (x: string, y: number) => string };
2626
var r3 = foo(arg3); // error
2727
~~~~

tests/baselines/reference/genericCallWithFunctionTypedArguments2.types

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ var a: {
5353
}
5454

5555
var r = foo(i); // any
56-
>r : any
57-
>foo(i) : any
56+
>r : {}
57+
>foo(i) : {}
5858
>foo : <T>(x: new (a: T) => T) => T
5959
>i : I
6060

@@ -71,8 +71,8 @@ var r3 = foo(i2); // string
7171
>i2 : I2<string>
7272

7373
var r3b = foo(a); // any
74-
>r3b : any
75-
>foo(a) : any
74+
>r3b : {}
75+
>foo(a) : {}
7676
>foo : <T>(x: new (a: T) => T) => T
7777
>a : new <T>(x: T) => T
7878

@@ -101,15 +101,15 @@ var r4 = foo2(1, i2); // error
101101
>i2 : I2<string>
102102

103103
var r4b = foo2(1, a); // any
104-
>r4b : any
105-
>foo2(1, a) : any
104+
>r4b : {}
105+
>foo2(1, a) : {}
106106
>foo2 : <T, U>(x: T, cb: new (a: T) => U) => U
107107
>1 : 1
108108
>a : new <T>(x: T) => T
109109

110110
var r5 = foo2(1, i); // any
111-
>r5 : any
112-
>foo2(1, i) : any
111+
>r5 : {}
112+
>foo2(1, i) : {}
113113
>foo2 : <T, U>(x: T, cb: new (a: T) => U) => U
114114
>1 : 1
115115
>i : I
@@ -141,16 +141,16 @@ function foo3<T, U>(x: T, cb: new(a: T) => U, y: U) {
141141
}
142142

143143
var r7 = foo3(null, i, ''); // any
144-
>r7 : any
145-
>foo3(null, i, '') : any
144+
>r7 : {}
145+
>foo3(null, i, '') : {}
146146
>foo3 : <T, U>(x: T, cb: new (a: T) => U, y: U) => U
147147
>null : null
148148
>i : I
149149
>'' : ""
150150

151151
var r7b = foo3(null, a, ''); // any
152-
>r7b : any
153-
>foo3(null, a, '') : any
152+
>r7b : {}
153+
>foo3(null, a, '') : {}
154154
>foo3 : <T, U>(x: T, cb: new (a: T) => U, y: U) => U
155155
>null : null
156156
>a : new <T>(x: T) => T

tests/baselines/reference/genericCallWithOverloadedConstructorTypedArguments.types

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,8 @@ module GenericParameter {
8787
>T : T
8888

8989
var r7 = foo5(b); // new any => string; new(x:number) => any
90-
>r7 : { new (x: any): string; new (x: number): any; }
91-
>foo5(b) : { new (x: any): string; new (x: number): any; }
90+
>r7 : { new (x: {}): string; new (x: number): {}; }
91+
>foo5(b) : { new (x: {}): string; new (x: number): {}; }
9292
>foo5 : <T>(cb: { new (x: T): string; new (x: number): T; }) => { new (x: T): string; new (x: number): T; }
9393
>b : { new <T>(x: T): string; new <T>(x: number): T; }
9494

@@ -114,8 +114,8 @@ module GenericParameter {
114114
>a : { new (x: boolean): string; new (x: number): boolean; }
115115

116116
var r9 = foo6(b); // new any => string; new(x:any, y?:any) => string
117-
>r9 : { new (x: any): string; new (x: any, y?: any): string; }
118-
>foo6(b) : { new (x: any): string; new (x: any, y?: any): string; }
117+
>r9 : { new (x: {}): string; new (x: {}, y?: {}): string; }
118+
>foo6(b) : { new (x: {}): string; new (x: {}, y?: {}): string; }
119119
>foo6 : <T>(cb: { new (x: T): string; new (x: T, y?: T): string; }) => { new (x: T): string; new (x: T, y?: T): string; }
120120
>b : { new <T>(x: T): string; new <T>(x: number): T; }
121121

@@ -137,8 +137,8 @@ module GenericParameter {
137137
}
138138

139139
var r13 = foo7(1, b); // new any => string; new(x:any, y?:any) => string
140-
>r13 : { new (x: any): string; new (x: any, y?: any): string; }
141-
>foo7(1, b) : { new (x: any): string; new (x: any, y?: any): string; }
140+
>r13 : { new (x: {}): string; new (x: {}, y?: {}): string; }
141+
>foo7(1, b) : { new (x: {}): string; new (x: {}, y?: {}): string; }
142142
>foo7 : <T>(x: T, cb: { new (x: T): string; new (x: T, y?: T): string; }) => { new (x: T): string; new (x: T, y?: T): string; }
143143
>1 : 1
144144
>b : { new <T>(x: T): string; new <T>(x: number): T; }
@@ -162,15 +162,15 @@ module GenericParameter {
162162
>T : T
163163

164164
var r14 = foo7(1, c); // new any => string; new(x:any, y?:any) => string
165-
>r14 : { new (x: any): string; new (x: any, y?: any): string; }
166-
>foo7(1, c) : { new (x: any): string; new (x: any, y?: any): string; }
165+
>r14 : { new (x: {}): string; new (x: {}, y?: {}): string; }
166+
>foo7(1, c) : { new (x: {}): string; new (x: {}, y?: {}): string; }
167167
>foo7 : <T>(x: T, cb: { new (x: T): string; new (x: T, y?: T): string; }) => { new (x: T): string; new (x: T, y?: T): string; }
168168
>1 : 1
169169
>c : { <T>(x: number): T; new <T>(x: T): string; }
170170

171171
var r15 = foo7(1, c2); // new any => string; new(x:any, y?:any) => string
172-
>r15 : { new (x: any): string; new (x: any, y?: any): string; }
173-
>foo7(1, c2) : { new (x: any): string; new (x: any, y?: any): string; }
172+
>r15 : { new (x: {}): string; new (x: {}, y?: {}): string; }
173+
>foo7(1, c2) : { new (x: {}): string; new (x: {}, y?: {}): string; }
174174
>foo7 : <T>(x: T, cb: { new (x: T): string; new (x: T, y?: T): string; }) => { new (x: T): string; new (x: T, y?: T): string; }
175175
>1 : 1
176176
>c2 : { new <T>(x: T): string; new <T>(x: number): T; }

tests/baselines/reference/genericCallWithOverloadedConstructorTypedArguments2.errors.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithOverloadedConstructorTypedArguments2.ts(31,20): error TS2345: Argument of type 'new <T>(x: T, y: T) => string' is not assignable to parameter of type '{ new (x: any): string; new (x: any, y?: any): string; }'.
1+
tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithOverloadedConstructorTypedArguments2.ts(31,20): error TS2345: Argument of type 'new <T>(x: T, y: T) => string' is not assignable to parameter of type '{ new (x: {}): string; new (x: {}, y?: {}): string; }'.
22

33

44
==== tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithOverloadedConstructorTypedArguments2.ts (1 errors) ====
@@ -34,7 +34,7 @@ tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithOve
3434
var b: { new <T>(x: T, y: T): string };
3535
var r10 = foo6(b); // error
3636
~
37-
!!! error TS2345: Argument of type 'new <T>(x: T, y: T) => string' is not assignable to parameter of type '{ new (x: any): string; new (x: any, y?: any): string; }'.
37+
!!! error TS2345: Argument of type 'new <T>(x: T, y: T) => string' is not assignable to parameter of type '{ new (x: {}): string; new (x: {}, y?: {}): string; }'.
3838

3939
function foo7<T>(x:T, cb: { new(x: T): string; new(x: T, y?: T): string }) {
4040
return cb;

tests/baselines/reference/genericCallWithOverloadedConstructorTypedArguments2.types

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,8 +63,8 @@ module GenericParameter {
6363
>T : T
6464

6565
var r6 = foo5(a); // ok
66-
>r6 : { new (x: any): string; new (x: number): any; }
67-
>foo5(a) : { new (x: any): string; new (x: number): any; }
66+
>r6 : { new (x: {}): string; new (x: number): {}; }
67+
>foo5(a) : { new (x: {}): string; new (x: number): {}; }
6868
>foo5 : <T>(cb: { new (x: T): string; new (x: number): T; }) => { new (x: T): string; new (x: number): T; }
6969
>a : new <T>(x: T) => T
7070

@@ -115,8 +115,8 @@ module GenericParameter {
115115
}
116116

117117
var r13 = foo7(1, a); // ok
118-
>r13 : { new (x: any): string; new (x: any, y?: any): string; }
119-
>foo7(1, a) : { new (x: any): string; new (x: any, y?: any): string; }
118+
>r13 : { new (x: {}): string; new (x: {}, y?: {}): string; }
119+
>foo7(1, a) : { new (x: {}): string; new (x: {}, y?: {}): string; }
120120
>foo7 : <T>(x: T, cb: { new (x: T): string; new (x: T, y?: T): string; }) => { new (x: T): string; new (x: T, y?: T): string; }
121121
>1 : 1
122122
>a : new <T>(x: T) => T
@@ -131,8 +131,8 @@ module GenericParameter {
131131
>T : T
132132

133133
var r14 = foo7(1, c); // ok
134-
>r14 : { new (x: any): string; new (x: any, y?: any): string; }
135-
>foo7(1, c) : { new (x: any): string; new (x: any, y?: any): string; }
134+
>r14 : { new (x: {}): string; new (x: {}, y?: {}): string; }
135+
>foo7(1, c) : { new (x: {}): string; new (x: {}, y?: {}): string; }
136136
>foo7 : <T>(x: T, cb: { new (x: T): string; new (x: T, y?: T): string; }) => { new (x: T): string; new (x: T, y?: T): string; }
137137
>1 : 1
138138
>c : { new <T>(x: T): number; new <T>(x: number): T; }

tests/baselines/reference/genericCallWithOverloadedFunctionTypedArguments.types

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,8 @@ module GenericParameter {
8383
>T : T
8484

8585
var r7 = foo5(a); // any => string (+1 overload)
86-
>r7 : { (x: any): string; (x: number): any; }
87-
>foo5(a) : { (x: any): string; (x: number): any; }
86+
>r7 : { (x: {}): string; (x: number): {}; }
87+
>foo5(a) : { (x: {}): string; (x: number): {}; }
8888
>foo5 : <T>(cb: { (x: T): string; (x: number): T; }) => { (x: T): string; (x: number): T; }
8989
>a : { <T>(x: T): string; <T>(x: number): T; }
9090

@@ -112,8 +112,8 @@ module GenericParameter {
112112
>x : any
113113

114114
var r9 = foo6(<T>(x: T) => ''); // any => string (+1 overload)
115-
>r9 : { (x: any): string; (x: any, y?: any): string; }
116-
>foo6(<T>(x: T) => '') : { (x: any): string; (x: any, y?: any): string; }
115+
>r9 : { (x: {}): string; (x: {}, y?: {}): string; }
116+
>foo6(<T>(x: T) => '') : { (x: {}): string; (x: {}, y?: {}): string; }
117117
>foo6 : <T>(cb: { (x: T): string; (x: T, y?: T): string; }) => { (x: T): string; (x: T, y?: T): string; }
118118
><T>(x: T) => '' : <T>(x: T) => string
119119
>T : T
@@ -122,8 +122,8 @@ module GenericParameter {
122122
>'' : ""
123123

124124
var r11 = foo6(<T>(x: T, y?: T) => ''); // any => string (+1 overload)
125-
>r11 : { (x: any): string; (x: any, y?: any): string; }
126-
>foo6(<T>(x: T, y?: T) => '') : { (x: any): string; (x: any, y?: any): string; }
125+
>r11 : { (x: {}): string; (x: {}, y?: {}): string; }
126+
>foo6(<T>(x: T, y?: T) => '') : { (x: {}): string; (x: {}, y?: {}): string; }
127127
>foo6 : <T>(cb: { (x: T): string; (x: T, y?: T): string; }) => { (x: T): string; (x: T, y?: T): string; }
128128
><T>(x: T, y?: T) => '' : <T>(x: T, y?: T) => string
129129
>T : T
@@ -160,8 +160,8 @@ module GenericParameter {
160160
>x : any
161161

162162
var r13 = foo7(1, <T>(x: T) => ''); // any => string (+1 overload) [inferences are made for T, but lambda not contextually typed]
163-
>r13 : { (x: any): string; (x: any, y?: any): string; }
164-
>foo7(1, <T>(x: T) => '') : { (x: any): string; (x: any, y?: any): string; }
163+
>r13 : { (x: {}): string; (x: {}, y?: {}): string; }
164+
>foo7(1, <T>(x: T) => '') : { (x: {}): string; (x: {}, y?: {}): string; }
165165
>foo7 : <T>(x: T, cb: { (x: T): string; (x: T, y?: T): string; }) => { (x: T): string; (x: T, y?: T): string; }
166166
>1 : 1
167167
><T>(x: T) => '' : <T>(x: T) => string
@@ -180,8 +180,8 @@ module GenericParameter {
180180
>T : T
181181

182182
var r14 = foo7(1, a); // any => string (+1 overload) [inferences are made for T, but lambda not contextually typed]
183-
>r14 : { (x: any): string; (x: any, y?: any): string; }
184-
>foo7(1, a) : { (x: any): string; (x: any, y?: any): string; }
183+
>r14 : { (x: {}): string; (x: {}, y?: {}): string; }
184+
>foo7(1, a) : { (x: {}): string; (x: {}, y?: {}): string; }
185185
>foo7 : <T>(x: T, cb: { (x: T): string; (x: T, y?: T): string; }) => { (x: T): string; (x: T, y?: T): string; }
186186
>1 : 1
187187
>a : { <T>(x: T): string; <T>(x: number): T; }

tests/baselines/reference/genericCallWithOverloadedFunctionTypedArguments2.errors.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithOverloadedFunctionTypedArguments2.ts(28,20): error TS2345: Argument of type '<T>(x: T, y: T) => string' is not assignable to parameter of type '{ (x: any): string; (x: any, y?: any): string; }'.
1+
tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithOverloadedFunctionTypedArguments2.ts(28,20): error TS2345: Argument of type '<T>(x: T, y: T) => string' is not assignable to parameter of type '{ (x: {}): string; (x: {}, y?: {}): string; }'.
22

33

44
==== tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithOverloadedFunctionTypedArguments2.ts (1 errors) ====
@@ -31,7 +31,7 @@ tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithOve
3131

3232
var r10 = foo6(<T>(x: T, y: T) => ''); // error
3333
~~~~~~~~~~~~~~~~~~~~~
34-
!!! error TS2345: Argument of type '<T>(x: T, y: T) => string' is not assignable to parameter of type '{ (x: any): string; (x: any, y?: any): string; }'.
34+
!!! error TS2345: Argument of type '<T>(x: T, y: T) => string' is not assignable to parameter of type '{ (x: {}): string; (x: {}, y?: {}): string; }'.
3535

3636
function foo7<T>(x:T, cb: { (x: T): string; (x: T, y?: T): string }) {
3737
return cb;

tests/baselines/reference/genericCallWithOverloadedFunctionTypedArguments2.types

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,8 @@ module GenericParameter {
5555
}
5656

5757
var r6 = foo5(<T>(x: T) => x); // ok
58-
>r6 : { (x: any): string; (x: number): any; }
59-
>foo5(<T>(x: T) => x) : { (x: any): string; (x: number): any; }
58+
>r6 : { (x: {}): string; (x: number): {}; }
59+
>foo5(<T>(x: T) => x) : { (x: {}): string; (x: number): {}; }
6060
>foo5 : <T>(cb: { (x: T): string; (x: number): T; }) => { (x: T): string; (x: number): T; }
6161
><T>(x: T) => x : <T>(x: T) => T
6262
>T : T
@@ -109,8 +109,8 @@ module GenericParameter {
109109
}
110110

111111
var r13 = foo7(1, <T>(x: T) => x); // ok
112-
>r13 : { (x: any): string; (x: any, y?: any): string; }
113-
>foo7(1, <T>(x: T) => x) : { (x: any): string; (x: any, y?: any): string; }
112+
>r13 : { (x: {}): string; (x: {}, y?: {}): string; }
113+
>foo7(1, <T>(x: T) => x) : { (x: {}): string; (x: {}, y?: {}): string; }
114114
>foo7 : <T>(x: T, cb: { (x: T): string; (x: T, y?: T): string; }) => { (x: T): string; (x: T, y?: T): string; }
115115
>1 : 1
116116
><T>(x: T) => x : <T>(x: T) => T
@@ -129,8 +129,8 @@ module GenericParameter {
129129
>T : T
130130

131131
var r14 = foo7(1, a); // ok
132-
>r14 : { (x: any): string; (x: any, y?: any): string; }
133-
>foo7(1, a) : { (x: any): string; (x: any, y?: any): string; }
132+
>r14 : { (x: {}): string; (x: {}, y?: {}): string; }
133+
>foo7(1, a) : { (x: {}): string; (x: {}, y?: {}): string; }
134134
>foo7 : <T>(x: T, cb: { (x: T): string; (x: T, y?: T): string; }) => { (x: T): string; (x: T, y?: T): string; }
135135
>1 : 1
136136
>a : { <T>(x: T): number; <T>(x: number): T; }
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
//// [genericFunctionParameters.ts]
2+
declare function f1<T>(cb: <S>(x: S) => T): T;
3+
declare function f2<T>(cb: <S extends number>(x: S) => T): T;
4+
declare function f3<T>(cb: <S extends Array<S>>(x: S) => T): T;
5+
6+
let x1 = f1(x => x); // {}
7+
let x2 = f2(x => x); // number
8+
let x3 = f3(x => x); // Array<any>
9+
10+
// Repro from #19345
11+
12+
declare const s: <R>(go: <S>(ops: { init(): S; }) => R) => R;
13+
const x = s(a => a.init()); // x is any, should have been {}
14+
15+
16+
//// [genericFunctionParameters.js]
17+
"use strict";
18+
var x1 = f1(function (x) { return x; }); // {}
19+
var x2 = f2(function (x) { return x; }); // number
20+
var x3 = f3(function (x) { return x; }); // Array<any>
21+
var x = s(function (a) { return a.init(); }); // x is any, should have been {}
22+
23+
24+
//// [genericFunctionParameters.d.ts]
25+
declare function f1<T>(cb: <S>(x: S) => T): T;
26+
declare function f2<T>(cb: <S extends number>(x: S) => T): T;
27+
declare function f3<T>(cb: <S extends Array<S>>(x: S) => T): T;
28+
declare let x1: {};
29+
declare let x2: number;
30+
declare let x3: any[];
31+
declare const s: <R>(go: <S>(ops: {
32+
init(): S;
33+
}) => R) => R;
34+
declare const x: {};

0 commit comments

Comments
 (0)