From bd1da15b5f4b06656e640a12a8eb40f03b202030 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 6 Mar 2020 09:53:53 -0800 Subject: [PATCH 1/2] Use erased signatures as inference targets --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 645bf049f4e12..7f56a22bdc501 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -18588,7 +18588,7 @@ namespace ts { const len = sourceLen < targetLen ? sourceLen : targetLen; const skipParameters = !!(getObjectFlags(source) & ObjectFlags.NonInferrableType); for (let i = 0; i < len; i++) { - inferFromSignature(getBaseSignature(sourceSignatures[sourceLen - len + i]), getBaseSignature(targetSignatures[targetLen - len + i]), skipParameters); + inferFromSignature(getBaseSignature(sourceSignatures[sourceLen - len + i]), getErasedSignature(targetSignatures[targetLen - len + i]), skipParameters); } } From 43ef6b772c88cdc43c966ec5976f902c80fed765 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 6 Mar 2020 10:05:54 -0800 Subject: [PATCH 2/2] Add tests --- .../reference/inferenceErasedSignatures.js | 76 ++++++++ .../inferenceErasedSignatures.symbols | 178 ++++++++++++++++++ .../reference/inferenceErasedSignatures.types | 118 ++++++++++++ .../compiler/inferenceErasedSignatures.ts | 55 ++++++ 4 files changed, 427 insertions(+) create mode 100644 tests/baselines/reference/inferenceErasedSignatures.js create mode 100644 tests/baselines/reference/inferenceErasedSignatures.symbols create mode 100644 tests/baselines/reference/inferenceErasedSignatures.types create mode 100644 tests/cases/compiler/inferenceErasedSignatures.ts diff --git a/tests/baselines/reference/inferenceErasedSignatures.js b/tests/baselines/reference/inferenceErasedSignatures.js new file mode 100644 index 0000000000000..651f1b6ca19e1 --- /dev/null +++ b/tests/baselines/reference/inferenceErasedSignatures.js @@ -0,0 +1,76 @@ +//// [inferenceErasedSignatures.ts] +// Repro from #37163 + +declare class SomeBaseClass { + set(key: K, value: this[K]): this[K]; +} + +abstract class SomeAbstractClass extends SomeBaseClass { + foo!: (r?: R) => void; + bar!: (r?: any) => void; + abstract baz(c: C): Promise; +} + +class SomeClass extends SomeAbstractClass { + async baz(context: number): Promise { + return `${context}`; + } +} + +type CType = T extends SomeAbstractClass ? C : never; +type MType = T extends SomeAbstractClass ? M : never; +type RType = T extends SomeAbstractClass ? R : never; + +type SomeClassC = CType; // number +type SomeClassM = MType; // string +type SomeClassR = RType; // boolean + +// Repro from #37163 + +interface BaseType { + set(key: K, value: this[K]): this[K]; + useT1(c: T1): void; + useT2(r?: T2): void; + unrelatedButSomehowRelevant(r?: any): void; +} + +interface InheritedType extends BaseType { + // This declaration shouldn't do anything... + useT1(_: number): void +} + +// Structural expansion of InheritedType +interface StructuralVersion { + set(key: K, value: this[K]): this[K]; + useT1(c: number): void; + useT2(r?: boolean): void; + unrelatedButSomehowRelevant(r?: any): void; +} + +type GetT1 = T extends BaseType ? U : never; + +type T1 = GetT1; // number +type T2 = GetT1; // number + + +//// [inferenceErasedSignatures.js] +"use strict"; +// Repro from #37163 +var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { + function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } + return new (P || (P = Promise))(function (resolve, reject) { + function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } + function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } + function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } + step((generator = generator.apply(thisArg, _arguments || [])).next()); + }); +}; +class SomeAbstractClass extends SomeBaseClass { +} +class SomeClass extends SomeAbstractClass { + baz(context) { + return __awaiter(this, void 0, void 0, function* () { + return `${context}`; + }); + } +} diff --git a/tests/baselines/reference/inferenceErasedSignatures.symbols b/tests/baselines/reference/inferenceErasedSignatures.symbols new file mode 100644 index 0000000000000..be05d2ccfcb8e --- /dev/null +++ b/tests/baselines/reference/inferenceErasedSignatures.symbols @@ -0,0 +1,178 @@ +=== tests/cases/compiler/inferenceErasedSignatures.ts === +// Repro from #37163 + +declare class SomeBaseClass { +>SomeBaseClass : Symbol(SomeBaseClass, Decl(inferenceErasedSignatures.ts, 0, 0)) + + set(key: K, value: this[K]): this[K]; +>set : Symbol(SomeBaseClass.set, Decl(inferenceErasedSignatures.ts, 2, 29)) +>K : Symbol(K, Decl(inferenceErasedSignatures.ts, 3, 8)) +>key : Symbol(key, Decl(inferenceErasedSignatures.ts, 3, 30)) +>K : Symbol(K, Decl(inferenceErasedSignatures.ts, 3, 8)) +>value : Symbol(value, Decl(inferenceErasedSignatures.ts, 3, 37)) +>K : Symbol(K, Decl(inferenceErasedSignatures.ts, 3, 8)) +>K : Symbol(K, Decl(inferenceErasedSignatures.ts, 3, 8)) +} + +abstract class SomeAbstractClass extends SomeBaseClass { +>SomeAbstractClass : Symbol(SomeAbstractClass, Decl(inferenceErasedSignatures.ts, 4, 1)) +>C : Symbol(C, Decl(inferenceErasedSignatures.ts, 6, 33)) +>M : Symbol(M, Decl(inferenceErasedSignatures.ts, 6, 35)) +>R : Symbol(R, Decl(inferenceErasedSignatures.ts, 6, 38)) +>SomeBaseClass : Symbol(SomeBaseClass, Decl(inferenceErasedSignatures.ts, 0, 0)) + + foo!: (r?: R) => void; +>foo : Symbol(SomeAbstractClass.foo, Decl(inferenceErasedSignatures.ts, 6, 65)) +>r : Symbol(r, Decl(inferenceErasedSignatures.ts, 7, 11)) +>R : Symbol(R, Decl(inferenceErasedSignatures.ts, 6, 38)) + + bar!: (r?: any) => void; +>bar : Symbol(SomeAbstractClass.bar, Decl(inferenceErasedSignatures.ts, 7, 26)) +>r : Symbol(r, Decl(inferenceErasedSignatures.ts, 8, 11)) + + abstract baz(c: C): Promise; +>baz : Symbol(SomeAbstractClass.baz, Decl(inferenceErasedSignatures.ts, 8, 28)) +>c : Symbol(c, Decl(inferenceErasedSignatures.ts, 9, 17)) +>C : Symbol(C, Decl(inferenceErasedSignatures.ts, 6, 33)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) +>M : Symbol(M, Decl(inferenceErasedSignatures.ts, 6, 35)) +} + +class SomeClass extends SomeAbstractClass { +>SomeClass : Symbol(SomeClass, Decl(inferenceErasedSignatures.ts, 10, 1)) +>SomeAbstractClass : Symbol(SomeAbstractClass, Decl(inferenceErasedSignatures.ts, 4, 1)) + + async baz(context: number): Promise { +>baz : Symbol(SomeClass.baz, Decl(inferenceErasedSignatures.ts, 12, 68)) +>context : Symbol(context, Decl(inferenceErasedSignatures.ts, 13, 14)) +>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --)) + + return `${context}`; +>context : Symbol(context, Decl(inferenceErasedSignatures.ts, 13, 14)) + } +} + +type CType = T extends SomeAbstractClass ? C : never; +>CType : Symbol(CType, Decl(inferenceErasedSignatures.ts, 16, 1)) +>T : Symbol(T, Decl(inferenceErasedSignatures.ts, 18, 11)) +>T : Symbol(T, Decl(inferenceErasedSignatures.ts, 18, 11)) +>SomeAbstractClass : Symbol(SomeAbstractClass, Decl(inferenceErasedSignatures.ts, 4, 1)) +>C : Symbol(C, Decl(inferenceErasedSignatures.ts, 18, 49)) +>C : Symbol(C, Decl(inferenceErasedSignatures.ts, 18, 49)) + +type MType = T extends SomeAbstractClass ? M : never; +>MType : Symbol(MType, Decl(inferenceErasedSignatures.ts, 18, 75)) +>T : Symbol(T, Decl(inferenceErasedSignatures.ts, 19, 11)) +>T : Symbol(T, Decl(inferenceErasedSignatures.ts, 19, 11)) +>SomeAbstractClass : Symbol(SomeAbstractClass, Decl(inferenceErasedSignatures.ts, 4, 1)) +>M : Symbol(M, Decl(inferenceErasedSignatures.ts, 19, 54)) +>M : Symbol(M, Decl(inferenceErasedSignatures.ts, 19, 54)) + +type RType = T extends SomeAbstractClass ? R : never; +>RType : Symbol(RType, Decl(inferenceErasedSignatures.ts, 19, 75)) +>T : Symbol(T, Decl(inferenceErasedSignatures.ts, 20, 11)) +>T : Symbol(T, Decl(inferenceErasedSignatures.ts, 20, 11)) +>SomeAbstractClass : Symbol(SomeAbstractClass, Decl(inferenceErasedSignatures.ts, 4, 1)) +>R : Symbol(R, Decl(inferenceErasedSignatures.ts, 20, 59)) +>R : Symbol(R, Decl(inferenceErasedSignatures.ts, 20, 59)) + +type SomeClassC = CType; // number +>SomeClassC : Symbol(SomeClassC, Decl(inferenceErasedSignatures.ts, 20, 75)) +>CType : Symbol(CType, Decl(inferenceErasedSignatures.ts, 16, 1)) +>SomeClass : Symbol(SomeClass, Decl(inferenceErasedSignatures.ts, 10, 1)) + +type SomeClassM = MType; // string +>SomeClassM : Symbol(SomeClassM, Decl(inferenceErasedSignatures.ts, 22, 35)) +>MType : Symbol(MType, Decl(inferenceErasedSignatures.ts, 18, 75)) +>SomeClass : Symbol(SomeClass, Decl(inferenceErasedSignatures.ts, 10, 1)) + +type SomeClassR = RType; // boolean +>SomeClassR : Symbol(SomeClassR, Decl(inferenceErasedSignatures.ts, 23, 35)) +>RType : Symbol(RType, Decl(inferenceErasedSignatures.ts, 19, 75)) +>SomeClass : Symbol(SomeClass, Decl(inferenceErasedSignatures.ts, 10, 1)) + +// Repro from #37163 + +interface BaseType { +>BaseType : Symbol(BaseType, Decl(inferenceErasedSignatures.ts, 24, 35)) +>T1 : Symbol(T1, Decl(inferenceErasedSignatures.ts, 28, 19)) +>T2 : Symbol(T2, Decl(inferenceErasedSignatures.ts, 28, 22)) + + set(key: K, value: this[K]): this[K]; +>set : Symbol(BaseType.set, Decl(inferenceErasedSignatures.ts, 28, 29)) +>K : Symbol(K, Decl(inferenceErasedSignatures.ts, 29, 8)) +>key : Symbol(key, Decl(inferenceErasedSignatures.ts, 29, 30)) +>K : Symbol(K, Decl(inferenceErasedSignatures.ts, 29, 8)) +>value : Symbol(value, Decl(inferenceErasedSignatures.ts, 29, 37)) +>K : Symbol(K, Decl(inferenceErasedSignatures.ts, 29, 8)) +>K : Symbol(K, Decl(inferenceErasedSignatures.ts, 29, 8)) + + useT1(c: T1): void; +>useT1 : Symbol(BaseType.useT1, Decl(inferenceErasedSignatures.ts, 29, 63)) +>c : Symbol(c, Decl(inferenceErasedSignatures.ts, 30, 10)) +>T1 : Symbol(T1, Decl(inferenceErasedSignatures.ts, 28, 19)) + + useT2(r?: T2): void; +>useT2 : Symbol(BaseType.useT2, Decl(inferenceErasedSignatures.ts, 30, 23)) +>r : Symbol(r, Decl(inferenceErasedSignatures.ts, 31, 10)) +>T2 : Symbol(T2, Decl(inferenceErasedSignatures.ts, 28, 22)) + + unrelatedButSomehowRelevant(r?: any): void; +>unrelatedButSomehowRelevant : Symbol(BaseType.unrelatedButSomehowRelevant, Decl(inferenceErasedSignatures.ts, 31, 24)) +>r : Symbol(r, Decl(inferenceErasedSignatures.ts, 32, 32)) +} + +interface InheritedType extends BaseType { +>InheritedType : Symbol(InheritedType, Decl(inferenceErasedSignatures.ts, 33, 1)) +>BaseType : Symbol(BaseType, Decl(inferenceErasedSignatures.ts, 24, 35)) + + // This declaration shouldn't do anything... + useT1(_: number): void +>useT1 : Symbol(InheritedType.useT1, Decl(inferenceErasedSignatures.ts, 35, 59)) +>_ : Symbol(_, Decl(inferenceErasedSignatures.ts, 37, 10)) +} + +// Structural expansion of InheritedType +interface StructuralVersion { +>StructuralVersion : Symbol(StructuralVersion, Decl(inferenceErasedSignatures.ts, 38, 1)) + + set(key: K, value: this[K]): this[K]; +>set : Symbol(StructuralVersion.set, Decl(inferenceErasedSignatures.ts, 41, 30)) +>K : Symbol(K, Decl(inferenceErasedSignatures.ts, 42, 8)) +>key : Symbol(key, Decl(inferenceErasedSignatures.ts, 42, 30)) +>K : Symbol(K, Decl(inferenceErasedSignatures.ts, 42, 8)) +>value : Symbol(value, Decl(inferenceErasedSignatures.ts, 42, 37)) +>K : Symbol(K, Decl(inferenceErasedSignatures.ts, 42, 8)) +>K : Symbol(K, Decl(inferenceErasedSignatures.ts, 42, 8)) + + useT1(c: number): void; +>useT1 : Symbol(StructuralVersion.useT1, Decl(inferenceErasedSignatures.ts, 42, 63)) +>c : Symbol(c, Decl(inferenceErasedSignatures.ts, 43, 10)) + + useT2(r?: boolean): void; +>useT2 : Symbol(StructuralVersion.useT2, Decl(inferenceErasedSignatures.ts, 43, 27)) +>r : Symbol(r, Decl(inferenceErasedSignatures.ts, 44, 10)) + + unrelatedButSomehowRelevant(r?: any): void; +>unrelatedButSomehowRelevant : Symbol(StructuralVersion.unrelatedButSomehowRelevant, Decl(inferenceErasedSignatures.ts, 44, 29)) +>r : Symbol(r, Decl(inferenceErasedSignatures.ts, 45, 32)) +} + +type GetT1 = T extends BaseType ? U : never; +>GetT1 : Symbol(GetT1, Decl(inferenceErasedSignatures.ts, 46, 1)) +>T : Symbol(T, Decl(inferenceErasedSignatures.ts, 48, 11)) +>T : Symbol(T, Decl(inferenceErasedSignatures.ts, 48, 11)) +>BaseType : Symbol(BaseType, Decl(inferenceErasedSignatures.ts, 24, 35)) +>U : Symbol(U, Decl(inferenceErasedSignatures.ts, 48, 40)) +>U : Symbol(U, Decl(inferenceErasedSignatures.ts, 48, 40)) + +type T1 = GetT1; // number +>T1 : Symbol(T1, Decl(inferenceErasedSignatures.ts, 48, 61)) +>GetT1 : Symbol(GetT1, Decl(inferenceErasedSignatures.ts, 46, 1)) +>InheritedType : Symbol(InheritedType, Decl(inferenceErasedSignatures.ts, 33, 1)) + +type T2 = GetT1; // number +>T2 : Symbol(T2, Decl(inferenceErasedSignatures.ts, 50, 31)) +>GetT1 : Symbol(GetT1, Decl(inferenceErasedSignatures.ts, 46, 1)) +>StructuralVersion : Symbol(StructuralVersion, Decl(inferenceErasedSignatures.ts, 38, 1)) + diff --git a/tests/baselines/reference/inferenceErasedSignatures.types b/tests/baselines/reference/inferenceErasedSignatures.types new file mode 100644 index 0000000000000..84f64ffda5682 --- /dev/null +++ b/tests/baselines/reference/inferenceErasedSignatures.types @@ -0,0 +1,118 @@ +=== tests/cases/compiler/inferenceErasedSignatures.ts === +// Repro from #37163 + +declare class SomeBaseClass { +>SomeBaseClass : SomeBaseClass + + set(key: K, value: this[K]): this[K]; +>set : (key: K, value: this[K]) => this[K] +>key : K +>value : this[K] +} + +abstract class SomeAbstractClass extends SomeBaseClass { +>SomeAbstractClass : SomeAbstractClass +>SomeBaseClass : SomeBaseClass + + foo!: (r?: R) => void; +>foo : (r?: R | undefined) => void +>r : R | undefined + + bar!: (r?: any) => void; +>bar : (r?: any) => void +>r : any + + abstract baz(c: C): Promise; +>baz : (c: C) => Promise +>c : C +} + +class SomeClass extends SomeAbstractClass { +>SomeClass : SomeClass +>SomeAbstractClass : SomeAbstractClass + + async baz(context: number): Promise { +>baz : (context: number) => Promise +>context : number + + return `${context}`; +>`${context}` : string +>context : number + } +} + +type CType = T extends SomeAbstractClass ? C : never; +>CType : CType + +type MType = T extends SomeAbstractClass ? M : never; +>MType : MType + +type RType = T extends SomeAbstractClass ? R : never; +>RType : RType + +type SomeClassC = CType; // number +>SomeClassC : number + +type SomeClassM = MType; // string +>SomeClassM : string + +type SomeClassR = RType; // boolean +>SomeClassR : boolean + +// Repro from #37163 + +interface BaseType { + set(key: K, value: this[K]): this[K]; +>set : (key: K, value: this[K]) => this[K] +>key : K +>value : this[K] + + useT1(c: T1): void; +>useT1 : (c: T1) => void +>c : T1 + + useT2(r?: T2): void; +>useT2 : (r?: T2 | undefined) => void +>r : T2 | undefined + + unrelatedButSomehowRelevant(r?: any): void; +>unrelatedButSomehowRelevant : (r?: any) => void +>r : any +} + +interface InheritedType extends BaseType { + // This declaration shouldn't do anything... + useT1(_: number): void +>useT1 : (_: number) => void +>_ : number +} + +// Structural expansion of InheritedType +interface StructuralVersion { + set(key: K, value: this[K]): this[K]; +>set : (key: K, value: this[K]) => this[K] +>key : K +>value : this[K] + + useT1(c: number): void; +>useT1 : (c: number) => void +>c : number + + useT2(r?: boolean): void; +>useT2 : (r?: boolean | undefined) => void +>r : boolean | undefined + + unrelatedButSomehowRelevant(r?: any): void; +>unrelatedButSomehowRelevant : (r?: any) => void +>r : any +} + +type GetT1 = T extends BaseType ? U : never; +>GetT1 : GetT1 + +type T1 = GetT1; // number +>T1 : number + +type T2 = GetT1; // number +>T2 : number + diff --git a/tests/cases/compiler/inferenceErasedSignatures.ts b/tests/cases/compiler/inferenceErasedSignatures.ts new file mode 100644 index 0000000000000..a5e18659c78c8 --- /dev/null +++ b/tests/cases/compiler/inferenceErasedSignatures.ts @@ -0,0 +1,55 @@ +// @strict: true +// @target: es2015 + +// Repro from #37163 + +declare class SomeBaseClass { + set(key: K, value: this[K]): this[K]; +} + +abstract class SomeAbstractClass extends SomeBaseClass { + foo!: (r?: R) => void; + bar!: (r?: any) => void; + abstract baz(c: C): Promise; +} + +class SomeClass extends SomeAbstractClass { + async baz(context: number): Promise { + return `${context}`; + } +} + +type CType = T extends SomeAbstractClass ? C : never; +type MType = T extends SomeAbstractClass ? M : never; +type RType = T extends SomeAbstractClass ? R : never; + +type SomeClassC = CType; // number +type SomeClassM = MType; // string +type SomeClassR = RType; // boolean + +// Repro from #37163 + +interface BaseType { + set(key: K, value: this[K]): this[K]; + useT1(c: T1): void; + useT2(r?: T2): void; + unrelatedButSomehowRelevant(r?: any): void; +} + +interface InheritedType extends BaseType { + // This declaration shouldn't do anything... + useT1(_: number): void +} + +// Structural expansion of InheritedType +interface StructuralVersion { + set(key: K, value: this[K]): this[K]; + useT1(c: number): void; + useT2(r?: boolean): void; + unrelatedButSomehowRelevant(r?: any): void; +} + +type GetT1 = T extends BaseType ? U : never; + +type T1 = GetT1; // number +type T2 = GetT1; // number