Skip to content

Commit fdf0095

Browse files
committed
Do not infer yield* type from contextual TReturn
1 parent 9370347 commit fdf0095

File tree

4 files changed

+220
-6
lines changed

4 files changed

+220
-6
lines changed

src/compiler/checker.ts

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30839,9 +30839,19 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3083930839
if (!node.asteriskToken && contextualReturnType.flags & TypeFlags.Union) {
3084030840
contextualReturnType = filterType(contextualReturnType, type => !!getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKind.Return, type, isAsyncGenerator));
3084130841
}
30842-
return node.asteriskToken
30843-
? contextualReturnType
30844-
: getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKind.Yield, contextualReturnType, isAsyncGenerator);
30842+
if (node.asteriskToken) {
30843+
const iterationTypes = getIterationTypesOfGeneratorFunctionReturnType(contextualReturnType, isAsyncGenerator);
30844+
const yieldType = iterationTypes?.yieldType ?? silentNeverType;
30845+
const returnType = getContextualType(node, contextFlags) ?? silentNeverType;
30846+
const nextType = iterationTypes?.nextType ?? unknownType;
30847+
const generatorType = createGeneratorType(yieldType, returnType, nextType, /*isAsyncGenerator*/ false);
30848+
if (isAsyncGenerator) {
30849+
const asyncGeneratorType = createGeneratorType(yieldType, returnType, nextType, /*isAsyncGenerator*/ true);
30850+
return getUnionType([generatorType, asyncGeneratorType]);
30851+
}
30852+
return generatorType;
30853+
}
30854+
return getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKind.Yield, contextualReturnType, isAsyncGenerator);
3084530855
}
3084630856
}
3084730857

@@ -37753,7 +37763,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3775337763
}
3775437764

3775537765
if (isGenerator) {
37756-
return createGeneratorReturnType(
37766+
return createGeneratorType(
3775737767
yieldType || neverType,
3775837768
returnType || fallbackReturnType,
3775937769
nextType || getContextualIterationType(IterationTypeKind.Next, func) || unknownType,
@@ -37770,7 +37780,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
3777037780
}
3777137781
}
3777237782

37773-
function createGeneratorReturnType(yieldType: Type, returnType: Type, nextType: Type, isAsyncGenerator: boolean) {
37783+
function createGeneratorType(yieldType: Type, returnType: Type, nextType: Type, isAsyncGenerator: boolean) {
3777437784
const resolver = isAsyncGenerator ? asyncIterationTypesResolver : syncIterationTypesResolver;
3777537785
const globalGeneratorType = resolver.getGlobalGeneratorType(/*reportErrors*/ false);
3777637786
yieldType = resolver.resolveIterationType(yieldType, /*errorNode*/ undefined) || unknownType;
@@ -40572,7 +40582,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4057240582
const generatorYieldType = getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKind.Yield, returnType, (functionFlags & FunctionFlags.Async) !== 0) || anyType;
4057340583
const generatorReturnType = getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKind.Return, returnType, (functionFlags & FunctionFlags.Async) !== 0) || generatorYieldType;
4057440584
const generatorNextType = getIterationTypeOfGeneratorFunctionReturnType(IterationTypeKind.Next, returnType, (functionFlags & FunctionFlags.Async) !== 0) || unknownType;
40575-
const generatorInstantiation = createGeneratorReturnType(generatorYieldType, generatorReturnType, generatorNextType, !!(functionFlags & FunctionFlags.Async));
40585+
const generatorInstantiation = createGeneratorType(generatorYieldType, generatorReturnType, generatorNextType, !!(functionFlags & FunctionFlags.Async));
4057640586

4057740587
return checkTypeAssignableTo(generatorInstantiation, returnType, errorNode);
4057840588
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
//// [tests/cases/compiler/asyncYieldStarContextualType.ts] ////
2+
3+
=== asyncYieldStarContextualType.ts ===
4+
// https://github.com/microsoft/TypeScript/issues/57903
5+
interface Result<T, E> {
6+
>Result : Symbol(Result, Decl(asyncYieldStarContextualType.ts, 0, 0))
7+
>T : Symbol(T, Decl(asyncYieldStarContextualType.ts, 1, 17))
8+
>E : Symbol(E, Decl(asyncYieldStarContextualType.ts, 1, 19))
9+
10+
[Symbol.iterator](): Generator<E, T, unknown>
11+
>[Symbol.iterator] : Symbol(Result[Symbol.iterator], Decl(asyncYieldStarContextualType.ts, 1, 24))
12+
>Symbol.iterator : Symbol(SymbolConstructor.iterator, Decl(lib.es2015.iterable.d.ts, --, --))
13+
>Symbol : Symbol(Symbol, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.symbol.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --), Decl(lib.es2019.symbol.d.ts, --, --))
14+
>iterator : Symbol(SymbolConstructor.iterator, Decl(lib.es2015.iterable.d.ts, --, --))
15+
>Generator : Symbol(Generator, Decl(lib.es2015.generator.d.ts, --, --))
16+
>E : Symbol(E, Decl(asyncYieldStarContextualType.ts, 1, 19))
17+
>T : Symbol(T, Decl(asyncYieldStarContextualType.ts, 1, 17))
18+
}
19+
20+
type Book = { id: string; title: string; authorId: string };
21+
>Book : Symbol(Book, Decl(asyncYieldStarContextualType.ts, 3, 1))
22+
>id : Symbol(id, Decl(asyncYieldStarContextualType.ts, 5, 13))
23+
>title : Symbol(title, Decl(asyncYieldStarContextualType.ts, 5, 25))
24+
>authorId : Symbol(authorId, Decl(asyncYieldStarContextualType.ts, 5, 40))
25+
26+
type Author = { id: string; name: string };
27+
>Author : Symbol(Author, Decl(asyncYieldStarContextualType.ts, 5, 60))
28+
>id : Symbol(id, Decl(asyncYieldStarContextualType.ts, 6, 15))
29+
>name : Symbol(name, Decl(asyncYieldStarContextualType.ts, 6, 27))
30+
31+
type BookWithAuthor = Book & { author: Author };
32+
>BookWithAuthor : Symbol(BookWithAuthor, Decl(asyncYieldStarContextualType.ts, 6, 43))
33+
>Book : Symbol(Book, Decl(asyncYieldStarContextualType.ts, 3, 1))
34+
>author : Symbol(author, Decl(asyncYieldStarContextualType.ts, 7, 30))
35+
>Author : Symbol(Author, Decl(asyncYieldStarContextualType.ts, 5, 60))
36+
37+
declare const authorPromise: Promise<Result<Author, "NOT_FOUND_AUTHOR">>;
38+
>authorPromise : Symbol(authorPromise, Decl(asyncYieldStarContextualType.ts, 9, 13))
39+
>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, --, --))
40+
>Result : Symbol(Result, Decl(asyncYieldStarContextualType.ts, 0, 0))
41+
>Author : Symbol(Author, Decl(asyncYieldStarContextualType.ts, 5, 60))
42+
43+
declare const mapper: <T>(result: Result<T, "NOT_FOUND_AUTHOR">) => Result<T, "NOT_FOUND_AUTHOR">;
44+
>mapper : Symbol(mapper, Decl(asyncYieldStarContextualType.ts, 10, 13))
45+
>T : Symbol(T, Decl(asyncYieldStarContextualType.ts, 10, 23))
46+
>result : Symbol(result, Decl(asyncYieldStarContextualType.ts, 10, 26))
47+
>Result : Symbol(Result, Decl(asyncYieldStarContextualType.ts, 0, 0))
48+
>T : Symbol(T, Decl(asyncYieldStarContextualType.ts, 10, 23))
49+
>Result : Symbol(Result, Decl(asyncYieldStarContextualType.ts, 0, 0))
50+
>T : Symbol(T, Decl(asyncYieldStarContextualType.ts, 10, 23))
51+
52+
async function* f(): AsyncGenerator<"NOT_FOUND_AUTHOR" | "NOT_FOUND_BOOK", BookWithAuthor, unknown> {
53+
>f : Symbol(f, Decl(asyncYieldStarContextualType.ts, 10, 98))
54+
>AsyncGenerator : Symbol(AsyncGenerator, Decl(lib.es2018.asyncgenerator.d.ts, --, --))
55+
>BookWithAuthor : Symbol(BookWithAuthor, Decl(asyncYieldStarContextualType.ts, 6, 43))
56+
57+
// Without yield*, the type of test1 is
58+
// Result<Author, "NOT_FOUND_AUTHOR>
59+
const test1 = await authorPromise.then(mapper)
60+
>test1 : Symbol(test1, Decl(asyncYieldStarContextualType.ts, 15, 9))
61+
>authorPromise.then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --))
62+
>authorPromise : Symbol(authorPromise, Decl(asyncYieldStarContextualType.ts, 9, 13))
63+
>then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --))
64+
>mapper : Symbol(mapper, Decl(asyncYieldStarContextualType.ts, 10, 13))
65+
66+
// With yield*, the type of test2 is
67+
// Author | BookWithAuthor
68+
// But this codepath has no way to produce BookWithAuthor
69+
const test2 = yield* await authorPromise.then(mapper)
70+
>test2 : Symbol(test2, Decl(asyncYieldStarContextualType.ts, 20, 9))
71+
>authorPromise.then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --))
72+
>authorPromise : Symbol(authorPromise, Decl(asyncYieldStarContextualType.ts, 9, 13))
73+
>then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --))
74+
>mapper : Symbol(mapper, Decl(asyncYieldStarContextualType.ts, 10, 13))
75+
76+
return null! as BookWithAuthor;
77+
>BookWithAuthor : Symbol(BookWithAuthor, Decl(asyncYieldStarContextualType.ts, 6, 43))
78+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
//// [tests/cases/compiler/asyncYieldStarContextualType.ts] ////
2+
3+
=== asyncYieldStarContextualType.ts ===
4+
// https://github.com/microsoft/TypeScript/issues/57903
5+
interface Result<T, E> {
6+
[Symbol.iterator](): Generator<E, T, unknown>
7+
>[Symbol.iterator] : () => Generator<E, T, unknown>
8+
> : ^^^^^^
9+
>Symbol.iterator : unique symbol
10+
> : ^^^^^^^^^^^^^
11+
>Symbol : SymbolConstructor
12+
> : ^^^^^^^^^^^^^^^^^
13+
>iterator : unique symbol
14+
> : ^^^^^^^^^^^^^
15+
}
16+
17+
type Book = { id: string; title: string; authorId: string };
18+
>Book : Book
19+
> : ^^^^
20+
>id : string
21+
> : ^^^^^^
22+
>title : string
23+
> : ^^^^^^
24+
>authorId : string
25+
> : ^^^^^^
26+
27+
type Author = { id: string; name: string };
28+
>Author : Author
29+
> : ^^^^^^
30+
>id : string
31+
> : ^^^^^^
32+
>name : string
33+
> : ^^^^^^
34+
35+
type BookWithAuthor = Book & { author: Author };
36+
>BookWithAuthor : BookWithAuthor
37+
> : ^^^^^^^^^^^^^^
38+
>author : Author
39+
> : ^^^^^^
40+
41+
declare const authorPromise: Promise<Result<Author, "NOT_FOUND_AUTHOR">>;
42+
>authorPromise : Promise<Result<Author, "NOT_FOUND_AUTHOR">>
43+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
44+
45+
declare const mapper: <T>(result: Result<T, "NOT_FOUND_AUTHOR">) => Result<T, "NOT_FOUND_AUTHOR">;
46+
>mapper : <T>(result: Result<T, "NOT_FOUND_AUTHOR">) => Result<T, "NOT_FOUND_AUTHOR">
47+
> : ^ ^^ ^^ ^^^^^
48+
>result : Result<T, "NOT_FOUND_AUTHOR">
49+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
50+
51+
async function* f(): AsyncGenerator<"NOT_FOUND_AUTHOR" | "NOT_FOUND_BOOK", BookWithAuthor, unknown> {
52+
>f : () => AsyncGenerator<"NOT_FOUND_AUTHOR" | "NOT_FOUND_BOOK", BookWithAuthor, unknown>
53+
> : ^^^^^^
54+
55+
// Without yield*, the type of test1 is
56+
// Result<Author, "NOT_FOUND_AUTHOR>
57+
const test1 = await authorPromise.then(mapper)
58+
>test1 : Result<Author, "NOT_FOUND_AUTHOR">
59+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
60+
>await authorPromise.then(mapper) : Result<Author, "NOT_FOUND_AUTHOR">
61+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
62+
>authorPromise.then(mapper) : Promise<Result<Author, "NOT_FOUND_AUTHOR">>
63+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
64+
>authorPromise.then : <TResult1 = Result<Author, "NOT_FOUND_AUTHOR">, TResult2 = never>(onfulfilled?: (value: Result<Author, "NOT_FOUND_AUTHOR">) => TResult1 | PromiseLike<TResult1>, onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2>) => Promise<TResult1 | TResult2>
65+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
66+
>authorPromise : Promise<Result<Author, "NOT_FOUND_AUTHOR">>
67+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
68+
>then : <TResult1 = Result<Author, "NOT_FOUND_AUTHOR">, TResult2 = never>(onfulfilled?: (value: Result<Author, "NOT_FOUND_AUTHOR">) => TResult1 | PromiseLike<TResult1>, onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2>) => Promise<TResult1 | TResult2>
69+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
70+
>mapper : <T>(result: Result<T, "NOT_FOUND_AUTHOR">) => Result<T, "NOT_FOUND_AUTHOR">
71+
> : ^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
72+
73+
// With yield*, the type of test2 is
74+
// Author | BookWithAuthor
75+
// But this codepath has no way to produce BookWithAuthor
76+
const test2 = yield* await authorPromise.then(mapper)
77+
>test2 : Author
78+
> : ^^^^^^
79+
>yield* await authorPromise.then(mapper) : Author
80+
> : ^^^^^^
81+
>await authorPromise.then(mapper) : Result<Author, "NOT_FOUND_AUTHOR">
82+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
83+
>authorPromise.then(mapper) : Promise<Result<Author, "NOT_FOUND_AUTHOR">>
84+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
85+
>authorPromise.then : <TResult1 = Result<Author, "NOT_FOUND_AUTHOR">, TResult2 = never>(onfulfilled?: (value: Result<Author, "NOT_FOUND_AUTHOR">) => TResult1 | PromiseLike<TResult1>, onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2>) => Promise<TResult1 | TResult2>
86+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
87+
>authorPromise : Promise<Result<Author, "NOT_FOUND_AUTHOR">>
88+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
89+
>then : <TResult1 = Result<Author, "NOT_FOUND_AUTHOR">, TResult2 = never>(onfulfilled?: (value: Result<Author, "NOT_FOUND_AUTHOR">) => TResult1 | PromiseLike<TResult1>, onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2>) => Promise<TResult1 | TResult2>
90+
> : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
91+
>mapper : <T>(result: Result<T, "NOT_FOUND_AUTHOR">) => Result<T, "NOT_FOUND_AUTHOR">
92+
> : ^ ^^ ^^ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
93+
94+
return null! as BookWithAuthor;
95+
>null! as BookWithAuthor : BookWithAuthor
96+
> : ^^^^^^^^^^^^^^
97+
>null! : null
98+
> : ^^^^
99+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// @target: esnext
2+
// @noEmit: true
3+
4+
// https://github.com/microsoft/TypeScript/issues/57903
5+
interface Result<T, E> {
6+
[Symbol.iterator](): Generator<E, T, unknown>
7+
}
8+
9+
type Book = { id: string; title: string; authorId: string };
10+
type Author = { id: string; name: string };
11+
type BookWithAuthor = Book & { author: Author };
12+
13+
declare const authorPromise: Promise<Result<Author, "NOT_FOUND_AUTHOR">>;
14+
declare const mapper: <T>(result: Result<T, "NOT_FOUND_AUTHOR">) => Result<T, "NOT_FOUND_AUTHOR">;
15+
16+
async function* f(): AsyncGenerator<"NOT_FOUND_AUTHOR" | "NOT_FOUND_BOOK", BookWithAuthor, unknown> {
17+
// Without yield*, the type of test1 is
18+
// Result<Author, "NOT_FOUND_AUTHOR>
19+
const test1 = await authorPromise.then(mapper)
20+
21+
// With yield*, the type of test2 is
22+
// Author | BookWithAuthor
23+
// But this codepath has no way to produce BookWithAuthor
24+
const test2 = yield* await authorPromise.then(mapper)
25+
26+
return null! as BookWithAuthor;
27+
}

0 commit comments

Comments
 (0)