Skip to content

Commit bdb8514

Browse files
authored
Fix contextual typing on yield and return expressions in generator function (microsoft#49736)
* add tests and initial fix * fix contextual return type in generator funcs * fix return statement contextual typing * filter using getiterationtypeofgeneratorfunctionreturntype * update baselines
1 parent 5c1abd3 commit bdb8514

14 files changed

+304
-19
lines changed

src/compiler/checker.ts

+13-6
Original file line numberDiff line numberDiff line change
@@ -26821,12 +26821,15 @@ namespace ts {
2682126821
if (contextualReturnType) {
2682226822
const functionFlags = getFunctionFlags(func);
2682326823
if (functionFlags & FunctionFlags.Generator) { // Generator or AsyncGenerator function
26824-
const use = functionFlags & FunctionFlags.Async ? IterationUse.AsyncGeneratorReturnType : IterationUse.GeneratorReturnType;
26825-
const iterationTypes = getIterationTypesOfIterable(contextualReturnType, use, /*errorNode*/ undefined);
26826-
if (!iterationTypes) {
26824+
const isAsyncGenerator = (functionFlags & FunctionFlags.Async) !== 0;
26825+
if (contextualReturnType.flags & TypeFlags.Union) {
26826+
contextualReturnType = filterType(contextualReturnType, type => !!getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKind.Return, type, isAsyncGenerator));
26827+
}
26828+
const iterationReturnType = getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKind.Return, contextualReturnType, (functionFlags & FunctionFlags.Async) !== 0);
26829+
if (!iterationReturnType) {
2682726830
return undefined;
2682826831
}
26829-
contextualReturnType = iterationTypes.returnType;
26832+
contextualReturnType = iterationReturnType;
2683026833
// falls through to unwrap Promise for AsyncGenerators
2683126834
}
2683226835

@@ -26855,11 +26858,15 @@ namespace ts {
2685526858
const func = getContainingFunction(node);
2685626859
if (func) {
2685726860
const functionFlags = getFunctionFlags(func);
26858-
const contextualReturnType = getContextualReturnType(func, contextFlags);
26861+
let contextualReturnType = getContextualReturnType(func, contextFlags);
2685926862
if (contextualReturnType) {
26863+
const isAsyncGenerator = (functionFlags & FunctionFlags.Async) !== 0;
26864+
if (!node.asteriskToken && contextualReturnType.flags & TypeFlags.Union) {
26865+
contextualReturnType = filterType(contextualReturnType, type => !!getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKind.Return, type, isAsyncGenerator));
26866+
}
2686026867
return node.asteriskToken
2686126868
? contextualReturnType
26862-
: getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKind.Yield, contextualReturnType, (functionFlags & FunctionFlags.Async) !== 0);
26869+
: getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKind.Yield, contextualReturnType, isAsyncGenerator);
2686326870
}
2686426871
}
2686526872

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
//// [contextualTypeOnYield1.ts]
2+
type FuncOrGeneratorFunc = () => (number | Generator<(arg: number) => void, any, void>)
3+
4+
const f: FuncOrGeneratorFunc = function*() {
5+
yield (num) => console.log(num); // `num` should be inferred to have type `number`.
6+
}
7+
8+
//// [contextualTypeOnYield1.js]
9+
"use strict";
10+
const f = function* () {
11+
yield (num) => console.log(num); // `num` should be inferred to have type `number`.
12+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
=== tests/cases/compiler/contextualTypeOnYield1.ts ===
2+
type FuncOrGeneratorFunc = () => (number | Generator<(arg: number) => void, any, void>)
3+
>FuncOrGeneratorFunc : Symbol(FuncOrGeneratorFunc, Decl(contextualTypeOnYield1.ts, 0, 0))
4+
>Generator : Symbol(Generator, Decl(lib.es2015.generator.d.ts, --, --))
5+
>arg : Symbol(arg, Decl(contextualTypeOnYield1.ts, 0, 54))
6+
7+
const f: FuncOrGeneratorFunc = function*() {
8+
>f : Symbol(f, Decl(contextualTypeOnYield1.ts, 2, 5))
9+
>FuncOrGeneratorFunc : Symbol(FuncOrGeneratorFunc, Decl(contextualTypeOnYield1.ts, 0, 0))
10+
11+
yield (num) => console.log(num); // `num` should be inferred to have type `number`.
12+
>num : Symbol(num, Decl(contextualTypeOnYield1.ts, 3, 9))
13+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
14+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
15+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
16+
>num : Symbol(num, Decl(contextualTypeOnYield1.ts, 3, 9))
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
=== tests/cases/compiler/contextualTypeOnYield1.ts ===
2+
type FuncOrGeneratorFunc = () => (number | Generator<(arg: number) => void, any, void>)
3+
>FuncOrGeneratorFunc : () => number | Generator<(arg: number) => void, any, void>
4+
>arg : number
5+
6+
const f: FuncOrGeneratorFunc = function*() {
7+
>f : FuncOrGeneratorFunc
8+
>function*() { yield (num) => console.log(num); // `num` should be inferred to have type `number`.} : () => Generator<(num: number) => void, void, unknown>
9+
10+
yield (num) => console.log(num); // `num` should be inferred to have type `number`.
11+
>yield (num) => console.log(num) : any
12+
>(num) => console.log(num) : (num: number) => void
13+
>num : number
14+
>console.log(num) : void
15+
>console.log : (...data: any[]) => void
16+
>console : Console
17+
>log : (...data: any[]) => void
18+
>num : number
19+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
//// [contextualTypeOnYield2.ts]
2+
type OrGen = () => (number | Generator<string, (arg: number) => void, undefined>);
3+
const g: OrGen = function* () {
4+
return (num) => console.log(num);
5+
}
6+
7+
//// [contextualTypeOnYield2.js]
8+
"use strict";
9+
const g = function* () {
10+
return (num) => console.log(num);
11+
};
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
=== tests/cases/compiler/contextualTypeOnYield2.ts ===
2+
type OrGen = () => (number | Generator<string, (arg: number) => void, undefined>);
3+
>OrGen : Symbol(OrGen, Decl(contextualTypeOnYield2.ts, 0, 0))
4+
>Generator : Symbol(Generator, Decl(lib.es2015.generator.d.ts, --, --))
5+
>arg : Symbol(arg, Decl(contextualTypeOnYield2.ts, 0, 48))
6+
7+
const g: OrGen = function* () {
8+
>g : Symbol(g, Decl(contextualTypeOnYield2.ts, 1, 5))
9+
>OrGen : Symbol(OrGen, Decl(contextualTypeOnYield2.ts, 0, 0))
10+
11+
return (num) => console.log(num);
12+
>num : Symbol(num, Decl(contextualTypeOnYield2.ts, 2, 12))
13+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
14+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
15+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
16+
>num : Symbol(num, Decl(contextualTypeOnYield2.ts, 2, 12))
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
=== tests/cases/compiler/contextualTypeOnYield2.ts ===
2+
type OrGen = () => (number | Generator<string, (arg: number) => void, undefined>);
3+
>OrGen : () => number | Generator<string, (arg: number) => void, undefined>
4+
>arg : number
5+
6+
const g: OrGen = function* () {
7+
>g : OrGen
8+
>function* () { return (num) => console.log(num);} : () => Generator<never, (num: number) => void, unknown>
9+
10+
return (num) => console.log(num);
11+
>(num) => console.log(num) : (num: number) => void
12+
>num : number
13+
>console.log(num) : void
14+
>console.log : (...data: any[]) => void
15+
>console : Console
16+
>log : (...data: any[]) => void
17+
>num : number
18+
}
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,53 @@
1-
tests/cases/conformance/generators/generatorReturnContextualType.ts(17,3): error TS2322: Type '{ x: string; }' is not assignable to type '{ x: "x"; }'.
1+
tests/cases/conformance/generators/generatorReturnContextualType.ts(29,3): error TS2322: Type '{ x: string; }' is not assignable to type '{ x: "x"; }'.
2+
Types of property 'x' are incompatible.
3+
Type 'string' is not assignable to type '"x"'.
4+
tests/cases/conformance/generators/generatorReturnContextualType.ts(34,3): error TS2322: Type '{ x: string; }' is not assignable to type '{ x: "x"; }'.
25
Types of property 'x' are incompatible.
36
Type 'string' is not assignable to type '"x"'.
47

58

6-
==== tests/cases/conformance/generators/generatorReturnContextualType.ts (1 errors) ====
9+
==== tests/cases/conformance/generators/generatorReturnContextualType.ts (2 errors) ====
710
// #35995
811

912
function* f1(): Generator<any, { x: 'x' }, any> {
1013
return { x: 'x' };
1114
}
1215

16+
function* g1(): Iterator<any, { x: 'x' }, any> {
17+
return { x: 'x' };
18+
}
19+
1320
async function* f2(): AsyncGenerator<any, { x: 'x' }, any> {
1421
return { x: 'x' };
1522
}
1623

24+
async function* g2(): AsyncIterator<any, { x: 'x' }, any> {
25+
return { x: 'x' };
26+
}
27+
1728
async function* f3(): AsyncGenerator<any, { x: 'x' }, any> {
1829
return Promise.resolve({ x: 'x' });
1930
}
2031

32+
async function* g3(): AsyncIterator<any, { x: 'x' }, any> {
33+
return Promise.resolve({ x: 'x' });
34+
}
35+
2136
async function* f4(): AsyncGenerator<any, { x: 'x' }, any> {
2237
const ret = { x: 'x' };
2338
return Promise.resolve(ret); // Error
2439
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
2540
!!! error TS2322: Type '{ x: string; }' is not assignable to type '{ x: "x"; }'.
2641
!!! error TS2322: Types of property 'x' are incompatible.
42+
!!! error TS2322: Type 'string' is not assignable to type '"x"'.
43+
}
44+
45+
async function* g4(): AsyncIterator<any, { x: 'x' }, any> {
46+
const ret = { x: 'x' };
47+
return Promise.resolve(ret); // Error
48+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
49+
!!! error TS2322: Type '{ x: string; }' is not assignable to type '{ x: "x"; }'.
50+
!!! error TS2322: Types of property 'x' are incompatible.
2751
!!! error TS2322: Type 'string' is not assignable to type '"x"'.
2852
}
2953

tests/baselines/reference/generatorReturnContextualType.js

+30
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,35 @@ function* f1(): Generator<any, { x: 'x' }, any> {
55
return { x: 'x' };
66
}
77

8+
function* g1(): Iterator<any, { x: 'x' }, any> {
9+
return { x: 'x' };
10+
}
11+
812
async function* f2(): AsyncGenerator<any, { x: 'x' }, any> {
913
return { x: 'x' };
1014
}
1115

16+
async function* g2(): AsyncIterator<any, { x: 'x' }, any> {
17+
return { x: 'x' };
18+
}
19+
1220
async function* f3(): AsyncGenerator<any, { x: 'x' }, any> {
1321
return Promise.resolve({ x: 'x' });
1422
}
1523

24+
async function* g3(): AsyncIterator<any, { x: 'x' }, any> {
25+
return Promise.resolve({ x: 'x' });
26+
}
27+
1628
async function* f4(): AsyncGenerator<any, { x: 'x' }, any> {
1729
const ret = { x: 'x' };
1830
return Promise.resolve(ret); // Error
1931
}
32+
33+
async function* g4(): AsyncIterator<any, { x: 'x' }, any> {
34+
const ret = { x: 'x' };
35+
return Promise.resolve(ret); // Error
36+
}
2037

2138

2239
//// [generatorReturnContextualType.js]
@@ -25,13 +42,26 @@ async function* f4(): AsyncGenerator<any, { x: 'x' }, any> {
2542
function* f1() {
2643
return { x: 'x' };
2744
}
45+
function* g1() {
46+
return { x: 'x' };
47+
}
2848
async function* f2() {
2949
return { x: 'x' };
3050
}
51+
async function* g2() {
52+
return { x: 'x' };
53+
}
3154
async function* f3() {
3255
return Promise.resolve({ x: 'x' });
3356
}
57+
async function* g3() {
58+
return Promise.resolve({ x: 'x' });
59+
}
3460
async function* f4() {
3561
const ret = { x: 'x' };
3662
return Promise.resolve(ret); // Error
3763
}
64+
async function* g4() {
65+
const ret = { x: 'x' };
66+
return Promise.resolve(ret); // Error
67+
}

tests/baselines/reference/generatorReturnContextualType.symbols

+57-11
Original file line numberDiff line numberDiff line change
@@ -10,40 +10,86 @@ function* f1(): Generator<any, { x: 'x' }, any> {
1010
>x : Symbol(x, Decl(generatorReturnContextualType.ts, 3, 10))
1111
}
1212

13+
function* g1(): Iterator<any, { x: 'x' }, any> {
14+
>g1 : Symbol(g1, Decl(generatorReturnContextualType.ts, 4, 1))
15+
>Iterator : Symbol(Iterator, Decl(lib.es2015.iterable.d.ts, --, --))
16+
>x : Symbol(x, Decl(generatorReturnContextualType.ts, 6, 31))
17+
18+
return { x: 'x' };
19+
>x : Symbol(x, Decl(generatorReturnContextualType.ts, 7, 10))
20+
}
21+
1322
async function* f2(): AsyncGenerator<any, { x: 'x' }, any> {
14-
>f2 : Symbol(f2, Decl(generatorReturnContextualType.ts, 4, 1))
23+
>f2 : Symbol(f2, Decl(generatorReturnContextualType.ts, 8, 1))
1524
>AsyncGenerator : Symbol(AsyncGenerator, Decl(lib.es2018.asyncgenerator.d.ts, --, --))
16-
>x : Symbol(x, Decl(generatorReturnContextualType.ts, 6, 43))
25+
>x : Symbol(x, Decl(generatorReturnContextualType.ts, 10, 43))
1726

1827
return { x: 'x' };
19-
>x : Symbol(x, Decl(generatorReturnContextualType.ts, 7, 10))
28+
>x : Symbol(x, Decl(generatorReturnContextualType.ts, 11, 10))
29+
}
30+
31+
async function* g2(): AsyncIterator<any, { x: 'x' }, any> {
32+
>g2 : Symbol(g2, Decl(generatorReturnContextualType.ts, 12, 1))
33+
>AsyncIterator : Symbol(AsyncIterator, Decl(lib.es2018.asynciterable.d.ts, --, --))
34+
>x : Symbol(x, Decl(generatorReturnContextualType.ts, 14, 42))
35+
36+
return { x: 'x' };
37+
>x : Symbol(x, Decl(generatorReturnContextualType.ts, 15, 10))
2038
}
2139

2240
async function* f3(): AsyncGenerator<any, { x: 'x' }, any> {
23-
>f3 : Symbol(f3, Decl(generatorReturnContextualType.ts, 8, 1))
41+
>f3 : Symbol(f3, Decl(generatorReturnContextualType.ts, 16, 1))
2442
>AsyncGenerator : Symbol(AsyncGenerator, Decl(lib.es2018.asyncgenerator.d.ts, --, --))
25-
>x : Symbol(x, Decl(generatorReturnContextualType.ts, 10, 43))
43+
>x : Symbol(x, Decl(generatorReturnContextualType.ts, 18, 43))
44+
45+
return Promise.resolve({ x: 'x' });
46+
>Promise.resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
47+
>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, --, --), Decl(lib.es2018.promise.d.ts, --, --))
48+
>resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
49+
>x : Symbol(x, Decl(generatorReturnContextualType.ts, 19, 26))
50+
}
51+
52+
async function* g3(): AsyncIterator<any, { x: 'x' }, any> {
53+
>g3 : Symbol(g3, Decl(generatorReturnContextualType.ts, 20, 1))
54+
>AsyncIterator : Symbol(AsyncIterator, Decl(lib.es2018.asynciterable.d.ts, --, --))
55+
>x : Symbol(x, Decl(generatorReturnContextualType.ts, 22, 42))
2656

2757
return Promise.resolve({ x: 'x' });
2858
>Promise.resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
2959
>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, --, --), Decl(lib.es2018.promise.d.ts, --, --))
3060
>resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
31-
>x : Symbol(x, Decl(generatorReturnContextualType.ts, 11, 26))
61+
>x : Symbol(x, Decl(generatorReturnContextualType.ts, 23, 26))
3262
}
3363

3464
async function* f4(): AsyncGenerator<any, { x: 'x' }, any> {
35-
>f4 : Symbol(f4, Decl(generatorReturnContextualType.ts, 12, 1))
65+
>f4 : Symbol(f4, Decl(generatorReturnContextualType.ts, 24, 1))
3666
>AsyncGenerator : Symbol(AsyncGenerator, Decl(lib.es2018.asyncgenerator.d.ts, --, --))
37-
>x : Symbol(x, Decl(generatorReturnContextualType.ts, 14, 43))
67+
>x : Symbol(x, Decl(generatorReturnContextualType.ts, 26, 43))
68+
69+
const ret = { x: 'x' };
70+
>ret : Symbol(ret, Decl(generatorReturnContextualType.ts, 27, 7))
71+
>x : Symbol(x, Decl(generatorReturnContextualType.ts, 27, 15))
72+
73+
return Promise.resolve(ret); // Error
74+
>Promise.resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
75+
>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, --, --), Decl(lib.es2018.promise.d.ts, --, --))
76+
>resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
77+
>ret : Symbol(ret, Decl(generatorReturnContextualType.ts, 27, 7))
78+
}
79+
80+
async function* g4(): AsyncIterator<any, { x: 'x' }, any> {
81+
>g4 : Symbol(g4, Decl(generatorReturnContextualType.ts, 29, 1))
82+
>AsyncIterator : Symbol(AsyncIterator, Decl(lib.es2018.asynciterable.d.ts, --, --))
83+
>x : Symbol(x, Decl(generatorReturnContextualType.ts, 31, 42))
3884

3985
const ret = { x: 'x' };
40-
>ret : Symbol(ret, Decl(generatorReturnContextualType.ts, 15, 7))
41-
>x : Symbol(x, Decl(generatorReturnContextualType.ts, 15, 15))
86+
>ret : Symbol(ret, Decl(generatorReturnContextualType.ts, 32, 7))
87+
>x : Symbol(x, Decl(generatorReturnContextualType.ts, 32, 15))
4288

4389
return Promise.resolve(ret); // Error
4490
>Promise.resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
4591
>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, --, --), Decl(lib.es2018.promise.d.ts, --, --))
4692
>resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
47-
>ret : Symbol(ret, Decl(generatorReturnContextualType.ts, 15, 7))
93+
>ret : Symbol(ret, Decl(generatorReturnContextualType.ts, 32, 7))
4894
}
4995

0 commit comments

Comments
 (0)