From e0599fd19cc690aca5fb74e18dbee5eed1b446a0 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 12 Jul 2019 11:11:36 -1000 Subject: [PATCH 1/3] Instantiate contextual type for return type in getReturnTypeFromBody --- src/compiler/checker.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b761606fcb3ef..1f8c62d0858cd 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -19051,7 +19051,7 @@ namespace ts { // If the given contextual type contains instantiable types and if a mapper representing // return type inferences is available, instantiate those types using that mapper. - function instantiateContextualType(contextualType: Type | undefined, node: Expression, contextFlags?: ContextFlags): Type | undefined { + function instantiateContextualType(contextualType: Type | undefined, node: Node, contextFlags?: ContextFlags): Type | undefined { if (contextualType && maybeTypeOfKind(contextualType, TypeFlags.Instantiable)) { const inferenceContext = getInferenceContext(node); // If no inferences have been made, nothing is gained from instantiating as type parameters @@ -23363,7 +23363,7 @@ namespace ts { nextType && isUnitType(nextType)) { const contextualType = !contextualSignature ? undefined : contextualSignature === getSignatureFromDeclaration(func) ? isGenerator ? undefined : returnType : - getReturnTypeOfSignature(contextualSignature); + instantiateContextualType(getReturnTypeOfSignature(contextualSignature), func); if (isGenerator) { yieldType = getWidenedLiteralLikeTypeForContextualIterationTypeIfNeeded(yieldType, contextualType, IterationTypeKind.Yield, isAsync); returnType = getWidenedLiteralLikeTypeForContextualIterationTypeIfNeeded(returnType, contextualType, IterationTypeKind.Return, isAsync); From 044d70fc243e7f68214796611d8b93c0e31dec65 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 12 Jul 2019 17:57:05 -1000 Subject: [PATCH 2/3] Add regression tests --- .../compiler/instantiateContextualTypes.ts | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/tests/cases/compiler/instantiateContextualTypes.ts b/tests/cases/compiler/instantiateContextualTypes.ts index b81356fa91ec9..3165011ecffea 100644 --- a/tests/cases/compiler/instantiateContextualTypes.ts +++ b/tests/cases/compiler/instantiateContextualTypes.ts @@ -140,3 +140,37 @@ declare function passContentsToFunc(outerBox: T, consumer: BoxConsumerFromOut declare const outerBoxOfString: OuterBox; passContentsToFunc(outerBoxOfString, box => box.value); + +// Repro from #32349 + +type DooDad = 'SOMETHING' | 'ELSE' ; + +class Interesting { + public compiles = () : Promise => { + return Promise.resolve().then(() => { + if (1 < 2) { + return 'SOMETHING'; + } + return 'ELSE'; + }); + }; + public doesnt = () : Promise => { + return Promise.resolve().then(() => { + return 'ELSE'; + }); + }; + public slightlyDifferentErrorMessage = () : Promise => { + return Promise.resolve().then(() => { + if (1 < 2) { + return 'SOMETHING'; + } + return 'SOMETHING'; + }); + }; +} + +// Repro from #32349 + +declare function invoke(f: () => T): T; + +let xx: 0 | 1 | 2 = invoke(() => 1); From 6f637b0870f27fcae5cfdc5ee97e212017e1ecde Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 12 Jul 2019 17:57:11 -1000 Subject: [PATCH 3/3] Accept new baselines --- .../reference/instantiateContextualTypes.js | 60 ++++++++++++ .../instantiateContextualTypes.symbols | 74 ++++++++++++++ .../instantiateContextualTypes.types | 96 +++++++++++++++++++ 3 files changed, 230 insertions(+) diff --git a/tests/baselines/reference/instantiateContextualTypes.js b/tests/baselines/reference/instantiateContextualTypes.js index 3a33b54278cd8..9de6bfb612c15 100644 --- a/tests/baselines/reference/instantiateContextualTypes.js +++ b/tests/baselines/reference/instantiateContextualTypes.js @@ -138,6 +138,40 @@ declare function passContentsToFunc(outerBox: T, consumer: BoxConsumerFromOut declare const outerBoxOfString: OuterBox; passContentsToFunc(outerBoxOfString, box => box.value); + +// Repro from #32349 + +type DooDad = 'SOMETHING' | 'ELSE' ; + +class Interesting { + public compiles = () : Promise => { + return Promise.resolve().then(() => { + if (1 < 2) { + return 'SOMETHING'; + } + return 'ELSE'; + }); + }; + public doesnt = () : Promise => { + return Promise.resolve().then(() => { + return 'ELSE'; + }); + }; + public slightlyDifferentErrorMessage = () : Promise => { + return Promise.resolve().then(() => { + if (1 < 2) { + return 'SOMETHING'; + } + return 'SOMETHING'; + }); + }; +} + +// Repro from #32349 + +declare function invoke(f: () => T): T; + +let xx: 0 | 1 | 2 = invoke(() => 1); //// [instantiateContextualTypes.js] @@ -162,3 +196,29 @@ var N1; createElement2(InferFunctionTypes, [(foo) => "" + foo]); })(N1 || (N1 = {})); passContentsToFunc(outerBoxOfString, box => box.value); +class Interesting { + constructor() { + this.compiles = () => { + return Promise.resolve().then(() => { + if (1 < 2) { + return 'SOMETHING'; + } + return 'ELSE'; + }); + }; + this.doesnt = () => { + return Promise.resolve().then(() => { + return 'ELSE'; + }); + }; + this.slightlyDifferentErrorMessage = () => { + return Promise.resolve().then(() => { + if (1 < 2) { + return 'SOMETHING'; + } + return 'SOMETHING'; + }); + }; + } +} +let xx = invoke(() => 1); diff --git a/tests/baselines/reference/instantiateContextualTypes.symbols b/tests/baselines/reference/instantiateContextualTypes.symbols index 098be95bac50c..1250edd585ba6 100644 --- a/tests/baselines/reference/instantiateContextualTypes.symbols +++ b/tests/baselines/reference/instantiateContextualTypes.symbols @@ -407,3 +407,77 @@ passContentsToFunc(outerBoxOfString, box => box.value); >box : Symbol(box, Decl(instantiateContextualTypes.ts, 138, 36)) >value : Symbol(value, Decl(instantiateContextualTypes.ts, 121, 20)) +// Repro from #32349 + +type DooDad = 'SOMETHING' | 'ELSE' ; +>DooDad : Symbol(DooDad, Decl(instantiateContextualTypes.ts, 138, 55)) + +class Interesting { +>Interesting : Symbol(Interesting, Decl(instantiateContextualTypes.ts, 142, 36)) + + public compiles = () : Promise => { +>compiles : Symbol(Interesting.compiles, Decl(instantiateContextualTypes.ts, 144, 19)) +>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, --, --)) +>DooDad : Symbol(DooDad, Decl(instantiateContextualTypes.ts, 138, 55)) + + return Promise.resolve().then(() => { +>Promise.resolve().then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --)) +>Promise.resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --)) +>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, --, --)) +>resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --)) +>then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --)) + + if (1 < 2) { + return 'SOMETHING'; + } + return 'ELSE'; + }); + }; + public doesnt = () : Promise => { +>doesnt : Symbol(Interesting.doesnt, Decl(instantiateContextualTypes.ts, 152, 3)) +>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, --, --)) +>DooDad : Symbol(DooDad, Decl(instantiateContextualTypes.ts, 138, 55)) + + return Promise.resolve().then(() => { +>Promise.resolve().then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --)) +>Promise.resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --)) +>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, --, --)) +>resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --)) +>then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --)) + + return 'ELSE'; + }); + }; + public slightlyDifferentErrorMessage = () : Promise => { +>slightlyDifferentErrorMessage : Symbol(Interesting.slightlyDifferentErrorMessage, Decl(instantiateContextualTypes.ts, 157, 3)) +>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, --, --)) +>DooDad : Symbol(DooDad, Decl(instantiateContextualTypes.ts, 138, 55)) + + return Promise.resolve().then(() => { +>Promise.resolve().then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --)) +>Promise.resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --)) +>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, --, --)) +>resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --)) +>then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --)) + + if (1 < 2) { + return 'SOMETHING'; + } + return 'SOMETHING'; + }); + }; +} + +// Repro from #32349 + +declare function invoke(f: () => T): T; +>invoke : Symbol(invoke, Decl(instantiateContextualTypes.ts, 166, 1)) +>T : Symbol(T, Decl(instantiateContextualTypes.ts, 170, 24)) +>f : Symbol(f, Decl(instantiateContextualTypes.ts, 170, 27)) +>T : Symbol(T, Decl(instantiateContextualTypes.ts, 170, 24)) +>T : Symbol(T, Decl(instantiateContextualTypes.ts, 170, 24)) + +let xx: 0 | 1 | 2 = invoke(() => 1); +>xx : Symbol(xx, Decl(instantiateContextualTypes.ts, 172, 3)) +>invoke : Symbol(invoke, Decl(instantiateContextualTypes.ts, 166, 1)) + diff --git a/tests/baselines/reference/instantiateContextualTypes.types b/tests/baselines/reference/instantiateContextualTypes.types index 0e755e82ed308..88e6ca7e4ddd5 100644 --- a/tests/baselines/reference/instantiateContextualTypes.types +++ b/tests/baselines/reference/instantiateContextualTypes.types @@ -326,3 +326,99 @@ passContentsToFunc(outerBoxOfString, box => box.value); >box : InnerBox >value : string +// Repro from #32349 + +type DooDad = 'SOMETHING' | 'ELSE' ; +>DooDad : DooDad + +class Interesting { +>Interesting : Interesting + + public compiles = () : Promise => { +>compiles : () => Promise +>() : Promise => { return Promise.resolve().then(() => { if (1 < 2) { return 'SOMETHING'; } return 'ELSE'; }); } : () => Promise + + return Promise.resolve().then(() => { +>Promise.resolve().then(() => { if (1 < 2) { return 'SOMETHING'; } return 'ELSE'; }) : Promise +>Promise.resolve().then : (onfulfilled?: ((value: void) => TResult1 | PromiseLike) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined) => Promise +>Promise.resolve() : Promise +>Promise.resolve : { (value: T | PromiseLike): Promise; (): Promise; } +>Promise : PromiseConstructor +>resolve : { (value: T | PromiseLike): Promise; (): Promise; } +>then : (onfulfilled?: ((value: void) => TResult1 | PromiseLike) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined) => Promise +>() => { if (1 < 2) { return 'SOMETHING'; } return 'ELSE'; } : () => "SOMETHING" | "ELSE" + + if (1 < 2) { +>1 < 2 : boolean +>1 : 1 +>2 : 2 + + return 'SOMETHING'; +>'SOMETHING' : "SOMETHING" + } + return 'ELSE'; +>'ELSE' : "ELSE" + + }); + }; + public doesnt = () : Promise => { +>doesnt : () => Promise +>() : Promise => { return Promise.resolve().then(() => { return 'ELSE'; }); } : () => Promise + + return Promise.resolve().then(() => { +>Promise.resolve().then(() => { return 'ELSE'; }) : Promise +>Promise.resolve().then : (onfulfilled?: ((value: void) => TResult1 | PromiseLike) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined) => Promise +>Promise.resolve() : Promise +>Promise.resolve : { (value: T | PromiseLike): Promise; (): Promise; } +>Promise : PromiseConstructor +>resolve : { (value: T | PromiseLike): Promise; (): Promise; } +>then : (onfulfilled?: ((value: void) => TResult1 | PromiseLike) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined) => Promise +>() => { return 'ELSE'; } : () => "ELSE" + + return 'ELSE'; +>'ELSE' : "ELSE" + + }); + }; + public slightlyDifferentErrorMessage = () : Promise => { +>slightlyDifferentErrorMessage : () => Promise +>() : Promise => { return Promise.resolve().then(() => { if (1 < 2) { return 'SOMETHING'; } return 'SOMETHING'; }); } : () => Promise + + return Promise.resolve().then(() => { +>Promise.resolve().then(() => { if (1 < 2) { return 'SOMETHING'; } return 'SOMETHING'; }) : Promise +>Promise.resolve().then : (onfulfilled?: ((value: void) => TResult1 | PromiseLike) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined) => Promise +>Promise.resolve() : Promise +>Promise.resolve : { (value: T | PromiseLike): Promise; (): Promise; } +>Promise : PromiseConstructor +>resolve : { (value: T | PromiseLike): Promise; (): Promise; } +>then : (onfulfilled?: ((value: void) => TResult1 | PromiseLike) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike) | null | undefined) => Promise +>() => { if (1 < 2) { return 'SOMETHING'; } return 'SOMETHING'; } : () => "SOMETHING" + + if (1 < 2) { +>1 < 2 : boolean +>1 : 1 +>2 : 2 + + return 'SOMETHING'; +>'SOMETHING' : "SOMETHING" + } + return 'SOMETHING'; +>'SOMETHING' : "SOMETHING" + + }); + }; +} + +// Repro from #32349 + +declare function invoke(f: () => T): T; +>invoke : (f: () => T) => T +>f : () => T + +let xx: 0 | 1 | 2 = invoke(() => 1); +>xx : 0 | 1 | 2 +>invoke(() => 1) : 1 +>invoke : (f: () => T) => T +>() => 1 : () => 1 +>1 : 1 +