Skip to content

Commit

Permalink
Merge pull request microsoft#19424 from Microsoft/inferenceFromGeneri…
Browse files Browse the repository at this point in the history
…cFunction

Improve inference from generic functions
  • Loading branch information
ahejlsberg authored Oct 23, 2017
2 parents ceba507 + 414f165 commit af940f1
Show file tree
Hide file tree
Showing 17 changed files with 291 additions and 79 deletions.
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

0 comments on commit af940f1

Please sign in to comment.