Skip to content

Commit 34dd4b8

Browse files
authored
Merge pull request microsoft#32386 from microsoft/fix32349
Instantiate contextual types for return expressions
2 parents 40fd4ef + 742caba commit 34dd4b8

File tree

5 files changed

+266
-2
lines changed

5 files changed

+266
-2
lines changed

src/compiler/checker.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -19119,7 +19119,7 @@ namespace ts {
1911919119

1912019120
// If the given contextual type contains instantiable types and if a mapper representing
1912119121
// return type inferences is available, instantiate those types using that mapper.
19122-
function instantiateContextualType(contextualType: Type | undefined, node: Expression, contextFlags?: ContextFlags): Type | undefined {
19122+
function instantiateContextualType(contextualType: Type | undefined, node: Node, contextFlags?: ContextFlags): Type | undefined {
1912319123
if (contextualType && maybeTypeOfKind(contextualType, TypeFlags.Instantiable)) {
1912419124
const inferenceContext = getInferenceContext(node);
1912519125
// If no inferences have been made, nothing is gained from instantiating as type parameters
@@ -23436,7 +23436,7 @@ namespace ts {
2343623436
nextType && isUnitType(nextType)) {
2343723437
const contextualType = !contextualSignature ? undefined :
2343823438
contextualSignature === getSignatureFromDeclaration(func) ? isGenerator ? undefined : returnType :
23439-
getReturnTypeOfSignature(contextualSignature);
23439+
instantiateContextualType(getReturnTypeOfSignature(contextualSignature), func);
2344023440
if (isGenerator) {
2344123441
yieldType = getWidenedLiteralLikeTypeForContextualIterationTypeIfNeeded(yieldType, contextualType, IterationTypeKind.Yield, isAsync);
2344223442
returnType = getWidenedLiteralLikeTypeForContextualIterationTypeIfNeeded(returnType, contextualType, IterationTypeKind.Return, isAsync);

tests/baselines/reference/instantiateContextualTypes.js

+60
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,40 @@ declare function passContentsToFunc<T>(outerBox: T, consumer: BoxConsumerFromOut
138138
declare const outerBoxOfString: OuterBox<string>;
139139

140140
passContentsToFunc(outerBoxOfString, box => box.value);
141+
142+
// Repro from #32349
143+
144+
type DooDad = 'SOMETHING' | 'ELSE' ;
145+
146+
class Interesting {
147+
public compiles = () : Promise<DooDad> => {
148+
return Promise.resolve().then(() => {
149+
if (1 < 2) {
150+
return 'SOMETHING';
151+
}
152+
return 'ELSE';
153+
});
154+
};
155+
public doesnt = () : Promise<DooDad> => {
156+
return Promise.resolve().then(() => {
157+
return 'ELSE';
158+
});
159+
};
160+
public slightlyDifferentErrorMessage = () : Promise<DooDad> => {
161+
return Promise.resolve().then(() => {
162+
if (1 < 2) {
163+
return 'SOMETHING';
164+
}
165+
return 'SOMETHING';
166+
});
167+
};
168+
}
169+
170+
// Repro from #32349
171+
172+
declare function invoke<T>(f: () => T): T;
173+
174+
let xx: 0 | 1 | 2 = invoke(() => 1);
141175

142176

143177
//// [instantiateContextualTypes.js]
@@ -162,3 +196,29 @@ var N1;
162196
createElement2(InferFunctionTypes, [(foo) => "" + foo]);
163197
})(N1 || (N1 = {}));
164198
passContentsToFunc(outerBoxOfString, box => box.value);
199+
class Interesting {
200+
constructor() {
201+
this.compiles = () => {
202+
return Promise.resolve().then(() => {
203+
if (1 < 2) {
204+
return 'SOMETHING';
205+
}
206+
return 'ELSE';
207+
});
208+
};
209+
this.doesnt = () => {
210+
return Promise.resolve().then(() => {
211+
return 'ELSE';
212+
});
213+
};
214+
this.slightlyDifferentErrorMessage = () => {
215+
return Promise.resolve().then(() => {
216+
if (1 < 2) {
217+
return 'SOMETHING';
218+
}
219+
return 'SOMETHING';
220+
});
221+
};
222+
}
223+
}
224+
let xx = invoke(() => 1);

tests/baselines/reference/instantiateContextualTypes.symbols

+74
Original file line numberDiff line numberDiff line change
@@ -407,3 +407,77 @@ passContentsToFunc(outerBoxOfString, box => box.value);
407407
>box : Symbol(box, Decl(instantiateContextualTypes.ts, 138, 36))
408408
>value : Symbol(value, Decl(instantiateContextualTypes.ts, 121, 20))
409409

410+
// Repro from #32349
411+
412+
type DooDad = 'SOMETHING' | 'ELSE' ;
413+
>DooDad : Symbol(DooDad, Decl(instantiateContextualTypes.ts, 138, 55))
414+
415+
class Interesting {
416+
>Interesting : Symbol(Interesting, Decl(instantiateContextualTypes.ts, 142, 36))
417+
418+
public compiles = () : Promise<DooDad> => {
419+
>compiles : Symbol(Interesting.compiles, Decl(instantiateContextualTypes.ts, 144, 19))
420+
>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, --, --))
421+
>DooDad : Symbol(DooDad, Decl(instantiateContextualTypes.ts, 138, 55))
422+
423+
return Promise.resolve().then(() => {
424+
>Promise.resolve().then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --))
425+
>Promise.resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
426+
>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, --, --))
427+
>resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
428+
>then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --))
429+
430+
if (1 < 2) {
431+
return 'SOMETHING';
432+
}
433+
return 'ELSE';
434+
});
435+
};
436+
public doesnt = () : Promise<DooDad> => {
437+
>doesnt : Symbol(Interesting.doesnt, Decl(instantiateContextualTypes.ts, 152, 3))
438+
>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, --, --))
439+
>DooDad : Symbol(DooDad, Decl(instantiateContextualTypes.ts, 138, 55))
440+
441+
return Promise.resolve().then(() => {
442+
>Promise.resolve().then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --))
443+
>Promise.resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
444+
>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, --, --))
445+
>resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
446+
>then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --))
447+
448+
return 'ELSE';
449+
});
450+
};
451+
public slightlyDifferentErrorMessage = () : Promise<DooDad> => {
452+
>slightlyDifferentErrorMessage : Symbol(Interesting.slightlyDifferentErrorMessage, Decl(instantiateContextualTypes.ts, 157, 3))
453+
>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, --, --))
454+
>DooDad : Symbol(DooDad, Decl(instantiateContextualTypes.ts, 138, 55))
455+
456+
return Promise.resolve().then(() => {
457+
>Promise.resolve().then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --))
458+
>Promise.resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
459+
>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, --, --))
460+
>resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
461+
>then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --))
462+
463+
if (1 < 2) {
464+
return 'SOMETHING';
465+
}
466+
return 'SOMETHING';
467+
});
468+
};
469+
}
470+
471+
// Repro from #32349
472+
473+
declare function invoke<T>(f: () => T): T;
474+
>invoke : Symbol(invoke, Decl(instantiateContextualTypes.ts, 166, 1))
475+
>T : Symbol(T, Decl(instantiateContextualTypes.ts, 170, 24))
476+
>f : Symbol(f, Decl(instantiateContextualTypes.ts, 170, 27))
477+
>T : Symbol(T, Decl(instantiateContextualTypes.ts, 170, 24))
478+
>T : Symbol(T, Decl(instantiateContextualTypes.ts, 170, 24))
479+
480+
let xx: 0 | 1 | 2 = invoke(() => 1);
481+
>xx : Symbol(xx, Decl(instantiateContextualTypes.ts, 172, 3))
482+
>invoke : Symbol(invoke, Decl(instantiateContextualTypes.ts, 166, 1))
483+

tests/baselines/reference/instantiateContextualTypes.types

+96
Original file line numberDiff line numberDiff line change
@@ -326,3 +326,99 @@ passContentsToFunc(outerBoxOfString, box => box.value);
326326
>box : InnerBox<string>
327327
>value : string
328328

329+
// Repro from #32349
330+
331+
type DooDad = 'SOMETHING' | 'ELSE' ;
332+
>DooDad : DooDad
333+
334+
class Interesting {
335+
>Interesting : Interesting
336+
337+
public compiles = () : Promise<DooDad> => {
338+
>compiles : () => Promise<DooDad>
339+
>() : Promise<DooDad> => { return Promise.resolve().then(() => { if (1 < 2) { return 'SOMETHING'; } return 'ELSE'; }); } : () => Promise<DooDad>
340+
341+
return Promise.resolve().then(() => {
342+
>Promise.resolve().then(() => { if (1 < 2) { return 'SOMETHING'; } return 'ELSE'; }) : Promise<DooDad>
343+
>Promise.resolve().then : <TResult1 = void, TResult2 = never>(onfulfilled?: ((value: void) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined) => Promise<TResult1 | TResult2>
344+
>Promise.resolve() : Promise<void>
345+
>Promise.resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
346+
>Promise : PromiseConstructor
347+
>resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
348+
>then : <TResult1 = void, TResult2 = never>(onfulfilled?: ((value: void) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined) => Promise<TResult1 | TResult2>
349+
>() => { if (1 < 2) { return 'SOMETHING'; } return 'ELSE'; } : () => "SOMETHING" | "ELSE"
350+
351+
if (1 < 2) {
352+
>1 < 2 : boolean
353+
>1 : 1
354+
>2 : 2
355+
356+
return 'SOMETHING';
357+
>'SOMETHING' : "SOMETHING"
358+
}
359+
return 'ELSE';
360+
>'ELSE' : "ELSE"
361+
362+
});
363+
};
364+
public doesnt = () : Promise<DooDad> => {
365+
>doesnt : () => Promise<DooDad>
366+
>() : Promise<DooDad> => { return Promise.resolve().then(() => { return 'ELSE'; }); } : () => Promise<DooDad>
367+
368+
return Promise.resolve().then(() => {
369+
>Promise.resolve().then(() => { return 'ELSE'; }) : Promise<DooDad>
370+
>Promise.resolve().then : <TResult1 = void, TResult2 = never>(onfulfilled?: ((value: void) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined) => Promise<TResult1 | TResult2>
371+
>Promise.resolve() : Promise<void>
372+
>Promise.resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
373+
>Promise : PromiseConstructor
374+
>resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
375+
>then : <TResult1 = void, TResult2 = never>(onfulfilled?: ((value: void) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined) => Promise<TResult1 | TResult2>
376+
>() => { return 'ELSE'; } : () => "ELSE"
377+
378+
return 'ELSE';
379+
>'ELSE' : "ELSE"
380+
381+
});
382+
};
383+
public slightlyDifferentErrorMessage = () : Promise<DooDad> => {
384+
>slightlyDifferentErrorMessage : () => Promise<DooDad>
385+
>() : Promise<DooDad> => { return Promise.resolve().then(() => { if (1 < 2) { return 'SOMETHING'; } return 'SOMETHING'; }); } : () => Promise<DooDad>
386+
387+
return Promise.resolve().then(() => {
388+
>Promise.resolve().then(() => { if (1 < 2) { return 'SOMETHING'; } return 'SOMETHING'; }) : Promise<DooDad>
389+
>Promise.resolve().then : <TResult1 = void, TResult2 = never>(onfulfilled?: ((value: void) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined) => Promise<TResult1 | TResult2>
390+
>Promise.resolve() : Promise<void>
391+
>Promise.resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
392+
>Promise : PromiseConstructor
393+
>resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
394+
>then : <TResult1 = void, TResult2 = never>(onfulfilled?: ((value: void) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null | undefined) => Promise<TResult1 | TResult2>
395+
>() => { if (1 < 2) { return 'SOMETHING'; } return 'SOMETHING'; } : () => "SOMETHING"
396+
397+
if (1 < 2) {
398+
>1 < 2 : boolean
399+
>1 : 1
400+
>2 : 2
401+
402+
return 'SOMETHING';
403+
>'SOMETHING' : "SOMETHING"
404+
}
405+
return 'SOMETHING';
406+
>'SOMETHING' : "SOMETHING"
407+
408+
});
409+
};
410+
}
411+
412+
// Repro from #32349
413+
414+
declare function invoke<T>(f: () => T): T;
415+
>invoke : <T>(f: () => T) => T
416+
>f : () => T
417+
418+
let xx: 0 | 1 | 2 = invoke(() => 1);
419+
>xx : 0 | 1 | 2
420+
>invoke(() => 1) : 1
421+
>invoke : <T>(f: () => T) => T
422+
>() => 1 : () => 1
423+
>1 : 1
424+

tests/cases/compiler/instantiateContextualTypes.ts

+34
Original file line numberDiff line numberDiff line change
@@ -140,3 +140,37 @@ declare function passContentsToFunc<T>(outerBox: T, consumer: BoxConsumerFromOut
140140
declare const outerBoxOfString: OuterBox<string>;
141141

142142
passContentsToFunc(outerBoxOfString, box => box.value);
143+
144+
// Repro from #32349
145+
146+
type DooDad = 'SOMETHING' | 'ELSE' ;
147+
148+
class Interesting {
149+
public compiles = () : Promise<DooDad> => {
150+
return Promise.resolve().then(() => {
151+
if (1 < 2) {
152+
return 'SOMETHING';
153+
}
154+
return 'ELSE';
155+
});
156+
};
157+
public doesnt = () : Promise<DooDad> => {
158+
return Promise.resolve().then(() => {
159+
return 'ELSE';
160+
});
161+
};
162+
public slightlyDifferentErrorMessage = () : Promise<DooDad> => {
163+
return Promise.resolve().then(() => {
164+
if (1 < 2) {
165+
return 'SOMETHING';
166+
}
167+
return 'SOMETHING';
168+
});
169+
};
170+
}
171+
172+
// Repro from #32349
173+
174+
declare function invoke<T>(f: () => T): T;
175+
176+
let xx: 0 | 1 | 2 = invoke(() => 1);

0 commit comments

Comments
 (0)