Skip to content

Improve inference from generic functions #19424

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Oct 23, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6726,6 +6726,16 @@ namespace ts {
isInJavaScriptFile(signature.declaration));
}

function getBaseSignature(signature: Signature) {
const typeParameters = signature.typeParameters;
if (typeParameters) {
const typeEraser = createTypeEraser(typeParameters);
const baseConstraints = map(typeParameters, tp => instantiateType(getBaseConstraintOfType(tp), typeEraser) || emptyObjectType);
return instantiateSignature(signature, createTypeMapper(typeParameters, baseConstraints), /*eraseTypeParameters*/ true);
}
return signature;
}

function getOrCreateTypeFromSignature(signature: Signature): ObjectType {
// There are two ways to declare a construct signature, one is by declaring a class constructor
// using the constructor keyword, and the other is declaring a bare construct signature in an
Expand Down Expand Up @@ -10955,7 +10965,7 @@ namespace ts {
const targetLen = targetSignatures.length;
const len = sourceLen < targetLen ? sourceLen : targetLen;
for (let i = 0; i < len; i++) {
inferFromSignature(getErasedSignature(sourceSignatures[sourceLen - len + i]), getErasedSignature(targetSignatures[targetLen - len + i]));
inferFromSignature(getBaseSignature(sourceSignatures[sourceLen - len + i]), getBaseSignature(targetSignatures[targetLen - len + i]));
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
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; }'.
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; }'.
Types of property 'cb' are incompatible.
Type 'new <T>(x: T, y: T) => string' is not assignable to type 'new (t: any) => string'.
Type 'new <T>(x: T, y: T) => string' is not assignable to type 'new (t: {}) => string'.
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; }'.
Types of property 'cb' are incompatible.
Type 'new (x: string, y: number) => string' is not assignable to type 'new (t: string) => string'.
Expand All @@ -19,9 +19,9 @@ tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithCon
var arg2: { cb: new <T>(x: T, y: T) => string };
var r2 = foo(arg2); // error
~~~~
!!! 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; }'.
!!! error TS2345: Argument of type '{ cb: new <T>(x: T, y: T) => string; }' is not assignable to parameter of type '{ cb: new (t: {}) => string; }'.
!!! error TS2345: Types of property 'cb' are incompatible.
!!! error TS2345: Type 'new <T>(x: T, y: T) => string' is not assignable to type 'new (t: any) => string'.
!!! error TS2345: Type 'new <T>(x: T, y: T) => string' is not assignable to type 'new (t: {}) => string'.
var arg3: { cb: new (x: string, y: number) => string };
var r3 = foo(arg3); // error
~~~~
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ var a: {
}

var r = foo(i); // any
>r : any
>foo(i) : any
>r : {}
>foo(i) : {}
>foo : <T>(x: new (a: T) => T) => T
>i : I

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

var r3b = foo(a); // any
>r3b : any
>foo(a) : any
>r3b : {}
>foo(a) : {}
>foo : <T>(x: new (a: T) => T) => T
>a : new <T>(x: T) => T

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

var r4b = foo2(1, a); // any
>r4b : any
>foo2(1, a) : any
>r4b : {}
>foo2(1, a) : {}
>foo2 : <T, U>(x: T, cb: new (a: T) => U) => U
>1 : 1
>a : new <T>(x: T) => T

var r5 = foo2(1, i); // any
>r5 : any
>foo2(1, i) : any
>r5 : {}
>foo2(1, i) : {}
>foo2 : <T, U>(x: T, cb: new (a: T) => U) => U
>1 : 1
>i : I
Expand Down Expand Up @@ -141,16 +141,16 @@ function foo3<T, U>(x: T, cb: new(a: T) => U, y: U) {
}

var r7 = foo3(null, i, ''); // any
>r7 : any
>foo3(null, i, '') : any
>r7 : {}
>foo3(null, i, '') : {}
>foo3 : <T, U>(x: T, cb: new (a: T) => U, y: U) => U
>null : null
>i : I
>'' : ""

var r7b = foo3(null, a, ''); // any
>r7b : any
>foo3(null, a, '') : any
>r7b : {}
>foo3(null, a, '') : {}
>foo3 : <T, U>(x: T, cb: new (a: T) => U, y: U) => U
>null : null
>a : new <T>(x: T) => T
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ module GenericParameter {
>T : T

var r7 = foo5(b); // new any => string; new(x:number) => any
>r7 : { new (x: any): string; new (x: number): any; }
>foo5(b) : { new (x: any): string; new (x: number): any; }
>r7 : { new (x: {}): string; new (x: number): {}; }
>foo5(b) : { new (x: {}): string; new (x: number): {}; }
>foo5 : <T>(cb: { new (x: T): string; new (x: number): T; }) => { new (x: T): string; new (x: number): T; }
>b : { new <T>(x: T): string; new <T>(x: number): T; }

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

var r9 = foo6(b); // new any => string; new(x:any, y?:any) => string
>r9 : { new (x: any): string; new (x: any, y?: any): string; }
>foo6(b) : { new (x: any): string; new (x: any, y?: any): string; }
>r9 : { new (x: {}): string; new (x: {}, y?: {}): string; }
>foo6(b) : { new (x: {}): string; new (x: {}, y?: {}): string; }
>foo6 : <T>(cb: { new (x: T): string; new (x: T, y?: T): string; }) => { new (x: T): string; new (x: T, y?: T): string; }
>b : { new <T>(x: T): string; new <T>(x: number): T; }

Expand All @@ -137,8 +137,8 @@ module GenericParameter {
}

var r13 = foo7(1, b); // new any => string; new(x:any, y?:any) => string
>r13 : { new (x: any): string; new (x: any, y?: any): string; }
>foo7(1, b) : { new (x: any): string; new (x: any, y?: any): string; }
>r13 : { new (x: {}): string; new (x: {}, y?: {}): string; }
>foo7(1, b) : { new (x: {}): string; new (x: {}, y?: {}): string; }
>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; }
>1 : 1
>b : { new <T>(x: T): string; new <T>(x: number): T; }
Expand All @@ -162,15 +162,15 @@ module GenericParameter {
>T : T

var r14 = foo7(1, c); // new any => string; new(x:any, y?:any) => string
>r14 : { new (x: any): string; new (x: any, y?: any): string; }
>foo7(1, c) : { new (x: any): string; new (x: any, y?: any): string; }
>r14 : { new (x: {}): string; new (x: {}, y?: {}): string; }
>foo7(1, c) : { new (x: {}): string; new (x: {}, y?: {}): string; }
>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; }
>1 : 1
>c : { <T>(x: number): T; new <T>(x: T): string; }

var r15 = foo7(1, c2); // new any => string; new(x:any, y?:any) => string
>r15 : { new (x: any): string; new (x: any, y?: any): string; }
>foo7(1, c2) : { new (x: any): string; new (x: any, y?: any): string; }
>r15 : { new (x: {}): string; new (x: {}, y?: {}): string; }
>foo7(1, c2) : { new (x: {}): string; new (x: {}, y?: {}): string; }
>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; }
>1 : 1
>c2 : { new <T>(x: T): string; new <T>(x: number): T; }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
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; }'.
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; }'.


==== tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithOverloadedConstructorTypedArguments2.ts (1 errors) ====
Expand Down Expand Up @@ -34,7 +34,7 @@ tests/cases/conformance/types/typeRelationships/typeInference/genericCallWithOve
var b: { new <T>(x: T, y: T): string };
var r10 = foo6(b); // error
~
!!! 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; }'.
!!! 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; }'.

function foo7<T>(x:T, cb: { new(x: T): string; new(x: T, y?: T): string }) {
return cb;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ module GenericParameter {
>T : T

var r6 = foo5(a); // ok
>r6 : { new (x: any): string; new (x: number): any; }
>foo5(a) : { new (x: any): string; new (x: number): any; }
>r6 : { new (x: {}): string; new (x: number): {}; }
>foo5(a) : { new (x: {}): string; new (x: number): {}; }
>foo5 : <T>(cb: { new (x: T): string; new (x: number): T; }) => { new (x: T): string; new (x: number): T; }
>a : new <T>(x: T) => T

Expand Down Expand Up @@ -115,8 +115,8 @@ module GenericParameter {
}

var r13 = foo7(1, a); // ok
>r13 : { new (x: any): string; new (x: any, y?: any): string; }
>foo7(1, a) : { new (x: any): string; new (x: any, y?: any): string; }
>r13 : { new (x: {}): string; new (x: {}, y?: {}): string; }
>foo7(1, a) : { new (x: {}): string; new (x: {}, y?: {}): string; }
>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; }
>1 : 1
>a : new <T>(x: T) => T
Expand All @@ -131,8 +131,8 @@ module GenericParameter {
>T : T

var r14 = foo7(1, c); // ok
>r14 : { new (x: any): string; new (x: any, y?: any): string; }
>foo7(1, c) : { new (x: any): string; new (x: any, y?: any): string; }
>r14 : { new (x: {}): string; new (x: {}, y?: {}): string; }
>foo7(1, c) : { new (x: {}): string; new (x: {}, y?: {}): string; }
>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; }
>1 : 1
>c : { new <T>(x: T): number; new <T>(x: number): T; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ module GenericParameter {
>T : T

var r7 = foo5(a); // any => string (+1 overload)
>r7 : { (x: any): string; (x: number): any; }
>foo5(a) : { (x: any): string; (x: number): any; }
>r7 : { (x: {}): string; (x: number): {}; }
>foo5(a) : { (x: {}): string; (x: number): {}; }
>foo5 : <T>(cb: { (x: T): string; (x: number): T; }) => { (x: T): string; (x: number): T; }
>a : { <T>(x: T): string; <T>(x: number): T; }

Expand Down Expand Up @@ -112,8 +112,8 @@ module GenericParameter {
>x : any

var r9 = foo6(<T>(x: T) => ''); // any => string (+1 overload)
>r9 : { (x: any): string; (x: any, y?: any): string; }
>foo6(<T>(x: T) => '') : { (x: any): string; (x: any, y?: any): string; }
>r9 : { (x: {}): string; (x: {}, y?: {}): string; }
>foo6(<T>(x: T) => '') : { (x: {}): string; (x: {}, y?: {}): string; }
>foo6 : <T>(cb: { (x: T): string; (x: T, y?: T): string; }) => { (x: T): string; (x: T, y?: T): string; }
><T>(x: T) => '' : <T>(x: T) => string
>T : T
Expand All @@ -122,8 +122,8 @@ module GenericParameter {
>'' : ""

var r11 = foo6(<T>(x: T, y?: T) => ''); // any => string (+1 overload)
>r11 : { (x: any): string; (x: any, y?: any): string; }
>foo6(<T>(x: T, y?: T) => '') : { (x: any): string; (x: any, y?: any): string; }
>r11 : { (x: {}): string; (x: {}, y?: {}): string; }
>foo6(<T>(x: T, y?: T) => '') : { (x: {}): string; (x: {}, y?: {}): string; }
>foo6 : <T>(cb: { (x: T): string; (x: T, y?: T): string; }) => { (x: T): string; (x: T, y?: T): string; }
><T>(x: T, y?: T) => '' : <T>(x: T, y?: T) => string
>T : T
Expand Down Expand Up @@ -160,8 +160,8 @@ module GenericParameter {
>x : any

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

var r14 = foo7(1, a); // any => string (+1 overload) [inferences are made for T, but lambda not contextually typed]
>r14 : { (x: any): string; (x: any, y?: any): string; }
>foo7(1, a) : { (x: any): string; (x: any, y?: any): string; }
>r14 : { (x: {}): string; (x: {}, y?: {}): string; }
>foo7(1, a) : { (x: {}): string; (x: {}, y?: {}): string; }
>foo7 : <T>(x: T, cb: { (x: T): string; (x: T, y?: T): string; }) => { (x: T): string; (x: T, y?: T): string; }
>1 : 1
>a : { <T>(x: T): string; <T>(x: number): T; }
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
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; }'.
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; }'.


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

var r10 = foo6(<T>(x: T, y: T) => ''); // error
~~~~~~~~~~~~~~~~~~~~~
!!! 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; }'.
!!! error TS2345: Argument of type '<T>(x: T, y: T) => string' is not assignable to parameter of type '{ (x: {}): string; (x: {}, y?: {}): string; }'.

function foo7<T>(x:T, cb: { (x: T): string; (x: T, y?: T): string }) {
return cb;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ module GenericParameter {
}

var r6 = foo5(<T>(x: T) => x); // ok
>r6 : { (x: any): string; (x: number): any; }
>foo5(<T>(x: T) => x) : { (x: any): string; (x: number): any; }
>r6 : { (x: {}): string; (x: number): {}; }
>foo5(<T>(x: T) => x) : { (x: {}): string; (x: number): {}; }
>foo5 : <T>(cb: { (x: T): string; (x: number): T; }) => { (x: T): string; (x: number): T; }
><T>(x: T) => x : <T>(x: T) => T
>T : T
Expand Down Expand Up @@ -109,8 +109,8 @@ module GenericParameter {
}

var r13 = foo7(1, <T>(x: T) => x); // ok
>r13 : { (x: any): string; (x: any, y?: any): string; }
>foo7(1, <T>(x: T) => x) : { (x: any): string; (x: any, y?: any): string; }
>r13 : { (x: {}): string; (x: {}, y?: {}): string; }
>foo7(1, <T>(x: T) => x) : { (x: {}): string; (x: {}, y?: {}): string; }
>foo7 : <T>(x: T, cb: { (x: T): string; (x: T, y?: T): string; }) => { (x: T): string; (x: T, y?: T): string; }
>1 : 1
><T>(x: T) => x : <T>(x: T) => T
Expand All @@ -129,8 +129,8 @@ module GenericParameter {
>T : T

var r14 = foo7(1, a); // ok
>r14 : { (x: any): string; (x: any, y?: any): string; }
>foo7(1, a) : { (x: any): string; (x: any, y?: any): string; }
>r14 : { (x: {}): string; (x: {}, y?: {}): string; }
>foo7(1, a) : { (x: {}): string; (x: {}, y?: {}): string; }
>foo7 : <T>(x: T, cb: { (x: T): string; (x: T, y?: T): string; }) => { (x: T): string; (x: T, y?: T): string; }
>1 : 1
>a : { <T>(x: T): number; <T>(x: number): T; }
Expand Down
34 changes: 34 additions & 0 deletions tests/baselines/reference/genericFunctionParameters.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//// [genericFunctionParameters.ts]
declare function f1<T>(cb: <S>(x: S) => T): T;
declare function f2<T>(cb: <S extends number>(x: S) => T): T;
declare function f3<T>(cb: <S extends Array<S>>(x: S) => T): T;

let x1 = f1(x => x); // {}
let x2 = f2(x => x); // number
let x3 = f3(x => x); // Array<any>

// Repro from #19345

declare const s: <R>(go: <S>(ops: { init(): S; }) => R) => R;
const x = s(a => a.init()); // x is any, should have been {}


//// [genericFunctionParameters.js]
"use strict";
var x1 = f1(function (x) { return x; }); // {}
var x2 = f2(function (x) { return x; }); // number
var x3 = f3(function (x) { return x; }); // Array<any>
var x = s(function (a) { return a.init(); }); // x is any, should have been {}


//// [genericFunctionParameters.d.ts]
declare function f1<T>(cb: <S>(x: S) => T): T;
declare function f2<T>(cb: <S extends number>(x: S) => T): T;
declare function f3<T>(cb: <S extends Array<S>>(x: S) => T): T;
declare let x1: {};
declare let x2: number;
declare let x3: any[];
declare const s: <R>(go: <S>(ops: {
init(): S;
}) => R) => R;
declare const x: {};
Loading