diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4cd242f264b1a..9347d53fd5056 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -1102,6 +1102,7 @@ const enum IterationUse { SpreadFlag = 1 << 5, DestructuringFlag = 1 << 6, PossiblyOutOfBounds = 1 << 7, + ReturnType = 1 << 8, // Spread, Destructuring, Array element assignment Element = AllowsSyncIterablesFlag, @@ -1114,8 +1115,8 @@ const enum IterationUse { YieldStar = AllowsSyncIterablesFlag | YieldStarFlag, AsyncYieldStar = AllowsSyncIterablesFlag | AllowsAsyncIterablesFlag | YieldStarFlag, - GeneratorReturnType = AllowsSyncIterablesFlag, - AsyncGeneratorReturnType = AllowsAsyncIterablesFlag, + GeneratorReturnType = AllowsSyncIterablesFlag | ReturnType, + AsyncGeneratorReturnType = AllowsAsyncIterablesFlag | ReturnType, } const enum IterationTypeKind { @@ -42859,8 +42860,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } const cacheKey = use & IterationUse.AllowsAsyncIterablesFlag ? "iterationTypesOfAsyncIterable" : "iterationTypesOfIterable"; - const cachedTypes = getCachedIterationTypes(type, cacheKey); - if (cachedTypes) return cachedTypes === noIterationTypes ? undefined : cachedTypes; + const isReturnType = use & IterationUse.ReturnType; + if (!isReturnType) { + // Do not use the cached value when collecting the iteration types of a return type as they may + // not consider all constituents. + const cachedTypes = getCachedIterationTypes(type, cacheKey); + if (cachedTypes) return cachedTypes === noIterationTypes ? undefined : cachedTypes; + } let allIterationTypes: IterationTypes[] | undefined; for (const constituent of (type as UnionType).types) { @@ -42873,8 +42879,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { addRelatedInfo(rootDiag, ...errorOutputContainer.errors); } } - setCachedIterationTypes(type, cacheKey, noIterationTypes); - return undefined; + if (!isReturnType) { + // Do not exit early or cache the iteration types of a return type as they consider all constituents. + setCachedIterationTypes(type, cacheKey, noIterationTypes); + return undefined; + } } else if (errorOutputContainer?.errors?.length) { for (const diag of errorOutputContainer.errors) { @@ -42886,7 +42895,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } const iterationTypes = allIterationTypes ? combineIterationTypes(allIterationTypes) : noIterationTypes; - setCachedIterationTypes(type, cacheKey, iterationTypes); + if (!isReturnType) { + // Do not cache the iteration types of a return type as they consider all constituents. Other usages + // will exit early when a non-iterable union member is found. + setCachedIterationTypes(type, cacheKey, iterationTypes); + } return iterationTypes === noIterationTypes ? undefined : iterationTypes; } diff --git a/tests/baselines/reference/contextualTypeOnYield1.types b/tests/baselines/reference/contextualTypeOnYield1.types index 870328bf65173..abca3a3176923 100644 --- a/tests/baselines/reference/contextualTypeOnYield1.types +++ b/tests/baselines/reference/contextualTypeOnYield1.types @@ -7,10 +7,10 @@ type FuncOrGeneratorFunc = () => (number | Generator<(arg: number) => void, any, const f: FuncOrGeneratorFunc = function*() { >f : FuncOrGeneratorFunc ->function*() { yield (num) => console.log(num); // `num` should be inferred to have type `number`.} : () => Generator<(num: number) => void, void, unknown> +>function*() { yield (num) => console.log(num); // `num` should be inferred to have type `number`.} : () => Generator<(num: number) => void, void, void> yield (num) => console.log(num); // `num` should be inferred to have type `number`. ->yield (num) => console.log(num) : any +>yield (num) => console.log(num) : void >(num) => console.log(num) : (num: number) => void >num : number >console.log(num) : void diff --git a/tests/baselines/reference/contextualTypeOnYield2.types b/tests/baselines/reference/contextualTypeOnYield2.types index 36299f95f2b54..0d54bdf7dd790 100644 --- a/tests/baselines/reference/contextualTypeOnYield2.types +++ b/tests/baselines/reference/contextualTypeOnYield2.types @@ -7,7 +7,7 @@ type OrGen = () => (number | Generator void, undefined> const g: OrGen = function* () { >g : OrGen ->function* () { return (num) => console.log(num);} : () => Generator void, unknown> +>function* () { return (num) => console.log(num);} : () => Generator void, undefined> return (num) => console.log(num); >(num) => console.log(num) : (num: number) => void diff --git a/tests/baselines/reference/generatorYieldContextualType.types b/tests/baselines/reference/generatorYieldContextualType.types index 84bac6e374eb5..e925716d97358 100644 --- a/tests/baselines/reference/generatorYieldContextualType.types +++ b/tests/baselines/reference/generatorYieldContextualType.types @@ -27,11 +27,11 @@ declare function f2(gen: () => Generator | AsyncGenerator(async function* () { >f2<0, 0, 1>(async function* () { const a = yield 0; return 0;}) : void >f2 : (gen: () => Generator | AsyncGenerator) => void ->async function* () { const a = yield 0; return 0;} : () => AsyncGenerator<0, 0, 1 | undefined> +>async function* () { const a = yield 0; return 0;} : () => AsyncGenerator<0, 0, 1> const a = yield 0; ->a : 1 | undefined ->yield 0 : 1 | undefined +>a : 1 +>yield 0 : 1 >0 : 0 return 0; diff --git a/tests/baselines/reference/typeOfYieldWithUnionInContextualReturnType.js b/tests/baselines/reference/typeOfYieldWithUnionInContextualReturnType.js new file mode 100644 index 0000000000000..97279e3d8a61f --- /dev/null +++ b/tests/baselines/reference/typeOfYieldWithUnionInContextualReturnType.js @@ -0,0 +1,73 @@ +//// [tests/cases/compiler/typeOfYieldWithUnionInContextualReturnType.ts] //// + +//// [typeOfYieldWithUnionInContextualReturnType.ts] +// https://github.com/microsoft/TypeScript/issues/42439 + +type SyncSequenceFactory = () => Generator; + +type AsyncSequenceFactory = () => AsyncGenerator; + +type SequenceFactory = SyncSequenceFactory | AsyncSequenceFactory + +const syncFactory: SyncSequenceFactory = function* (){ + let name = ""; + while(!name){ + name = yield "What is your name?" + } + return `That's the end of the game, ${name}` +} + +const asyncFactory: AsyncSequenceFactory = async function* (){ + let name = ""; + while(!name){ + name = yield "What is your name?" + } + return `That's the end of the game, ${name}` +} + +const looserSyncFactory: SequenceFactory = function* (){ + let name = ""; + while(!name){ + name = yield "What is your name?" + } + return `That's the end of the game, ${name}` +} + +const looserAsyncFactory: SequenceFactory = async function* (){ + let name = ""; + while(!name){ + name = yield "What is your name?" + } + return `That's the end of the game, ${name}` +} + +//// [typeOfYieldWithUnionInContextualReturnType.js] +// https://github.com/microsoft/TypeScript/issues/42439 +const syncFactory = function* () { + let name = ""; + while (!name) { + name = yield "What is your name?"; + } + return `That's the end of the game, ${name}`; +}; +const asyncFactory = async function* () { + let name = ""; + while (!name) { + name = yield "What is your name?"; + } + return `That's the end of the game, ${name}`; +}; +const looserSyncFactory = function* () { + let name = ""; + while (!name) { + name = yield "What is your name?"; + } + return `That's the end of the game, ${name}`; +}; +const looserAsyncFactory = async function* () { + let name = ""; + while (!name) { + name = yield "What is your name?"; + } + return `That's the end of the game, ${name}`; +}; diff --git a/tests/baselines/reference/typeOfYieldWithUnionInContextualReturnType.symbols b/tests/baselines/reference/typeOfYieldWithUnionInContextualReturnType.symbols new file mode 100644 index 0000000000000..0f02ce9e15785 --- /dev/null +++ b/tests/baselines/reference/typeOfYieldWithUnionInContextualReturnType.symbols @@ -0,0 +1,85 @@ +//// [tests/cases/compiler/typeOfYieldWithUnionInContextualReturnType.ts] //// + +=== typeOfYieldWithUnionInContextualReturnType.ts === +// https://github.com/microsoft/TypeScript/issues/42439 + +type SyncSequenceFactory = () => Generator; +>SyncSequenceFactory : Symbol(SyncSequenceFactory, Decl(typeOfYieldWithUnionInContextualReturnType.ts, 0, 0)) +>Generator : Symbol(Generator, Decl(lib.es2015.generator.d.ts, --, --)) + +type AsyncSequenceFactory = () => AsyncGenerator; +>AsyncSequenceFactory : Symbol(AsyncSequenceFactory, Decl(typeOfYieldWithUnionInContextualReturnType.ts, 2, 67)) +>AsyncGenerator : Symbol(AsyncGenerator, Decl(lib.es2018.asyncgenerator.d.ts, --, --)) + +type SequenceFactory = SyncSequenceFactory | AsyncSequenceFactory +>SequenceFactory : Symbol(SequenceFactory, Decl(typeOfYieldWithUnionInContextualReturnType.ts, 4, 73)) +>SyncSequenceFactory : Symbol(SyncSequenceFactory, Decl(typeOfYieldWithUnionInContextualReturnType.ts, 0, 0)) +>AsyncSequenceFactory : Symbol(AsyncSequenceFactory, Decl(typeOfYieldWithUnionInContextualReturnType.ts, 2, 67)) + +const syncFactory: SyncSequenceFactory = function* (){ +>syncFactory : Symbol(syncFactory, Decl(typeOfYieldWithUnionInContextualReturnType.ts, 8, 5)) +>SyncSequenceFactory : Symbol(SyncSequenceFactory, Decl(typeOfYieldWithUnionInContextualReturnType.ts, 0, 0)) + + let name = ""; +>name : Symbol(name, Decl(typeOfYieldWithUnionInContextualReturnType.ts, 9, 5)) + + while(!name){ +>name : Symbol(name, Decl(typeOfYieldWithUnionInContextualReturnType.ts, 9, 5)) + + name = yield "What is your name?" +>name : Symbol(name, Decl(typeOfYieldWithUnionInContextualReturnType.ts, 9, 5)) + } + return `That's the end of the game, ${name}` +>name : Symbol(name, Decl(typeOfYieldWithUnionInContextualReturnType.ts, 9, 5)) +} + +const asyncFactory: AsyncSequenceFactory = async function* (){ +>asyncFactory : Symbol(asyncFactory, Decl(typeOfYieldWithUnionInContextualReturnType.ts, 16, 5)) +>AsyncSequenceFactory : Symbol(AsyncSequenceFactory, Decl(typeOfYieldWithUnionInContextualReturnType.ts, 2, 67)) + + let name = ""; +>name : Symbol(name, Decl(typeOfYieldWithUnionInContextualReturnType.ts, 17, 5)) + + while(!name){ +>name : Symbol(name, Decl(typeOfYieldWithUnionInContextualReturnType.ts, 17, 5)) + + name = yield "What is your name?" +>name : Symbol(name, Decl(typeOfYieldWithUnionInContextualReturnType.ts, 17, 5)) + } + return `That's the end of the game, ${name}` +>name : Symbol(name, Decl(typeOfYieldWithUnionInContextualReturnType.ts, 17, 5)) +} + +const looserSyncFactory: SequenceFactory = function* (){ +>looserSyncFactory : Symbol(looserSyncFactory, Decl(typeOfYieldWithUnionInContextualReturnType.ts, 24, 5)) +>SequenceFactory : Symbol(SequenceFactory, Decl(typeOfYieldWithUnionInContextualReturnType.ts, 4, 73)) + + let name = ""; +>name : Symbol(name, Decl(typeOfYieldWithUnionInContextualReturnType.ts, 25, 5)) + + while(!name){ +>name : Symbol(name, Decl(typeOfYieldWithUnionInContextualReturnType.ts, 25, 5)) + + name = yield "What is your name?" +>name : Symbol(name, Decl(typeOfYieldWithUnionInContextualReturnType.ts, 25, 5)) + } + return `That's the end of the game, ${name}` +>name : Symbol(name, Decl(typeOfYieldWithUnionInContextualReturnType.ts, 25, 5)) +} + +const looserAsyncFactory: SequenceFactory = async function* (){ +>looserAsyncFactory : Symbol(looserAsyncFactory, Decl(typeOfYieldWithUnionInContextualReturnType.ts, 32, 5)) +>SequenceFactory : Symbol(SequenceFactory, Decl(typeOfYieldWithUnionInContextualReturnType.ts, 4, 73)) + + let name = ""; +>name : Symbol(name, Decl(typeOfYieldWithUnionInContextualReturnType.ts, 33, 5)) + + while(!name){ +>name : Symbol(name, Decl(typeOfYieldWithUnionInContextualReturnType.ts, 33, 5)) + + name = yield "What is your name?" +>name : Symbol(name, Decl(typeOfYieldWithUnionInContextualReturnType.ts, 33, 5)) + } + return `That's the end of the game, ${name}` +>name : Symbol(name, Decl(typeOfYieldWithUnionInContextualReturnType.ts, 33, 5)) +} diff --git a/tests/baselines/reference/typeOfYieldWithUnionInContextualReturnType.types b/tests/baselines/reference/typeOfYieldWithUnionInContextualReturnType.types new file mode 100644 index 0000000000000..3740eb7815dd6 --- /dev/null +++ b/tests/baselines/reference/typeOfYieldWithUnionInContextualReturnType.types @@ -0,0 +1,105 @@ +//// [tests/cases/compiler/typeOfYieldWithUnionInContextualReturnType.ts] //// + +=== typeOfYieldWithUnionInContextualReturnType.ts === +// https://github.com/microsoft/TypeScript/issues/42439 + +type SyncSequenceFactory = () => Generator; +>SyncSequenceFactory : () => Generator + +type AsyncSequenceFactory = () => AsyncGenerator; +>AsyncSequenceFactory : () => AsyncGenerator + +type SequenceFactory = SyncSequenceFactory | AsyncSequenceFactory +>SequenceFactory : SyncSequenceFactory | AsyncSequenceFactory + +const syncFactory: SyncSequenceFactory = function* (){ +>syncFactory : SyncSequenceFactory +>function* (){ let name = ""; while(!name){ name = yield "What is your name?" } return `That's the end of the game, ${name}`} : () => Generator + + let name = ""; +>name : string +>"" : "" + + while(!name){ +>!name : boolean +>name : string + + name = yield "What is your name?" +>name = yield "What is your name?" : string +>name : string +>yield "What is your name?" : string +>"What is your name?" : "What is your name?" + } + return `That's the end of the game, ${name}` +>`That's the end of the game, ${name}` : string +>name : string +} + +const asyncFactory: AsyncSequenceFactory = async function* (){ +>asyncFactory : AsyncSequenceFactory +>async function* (){ let name = ""; while(!name){ name = yield "What is your name?" } return `That's the end of the game, ${name}`} : () => AsyncGenerator + + let name = ""; +>name : string +>"" : "" + + while(!name){ +>!name : boolean +>name : string + + name = yield "What is your name?" +>name = yield "What is your name?" : string +>name : string +>yield "What is your name?" : string +>"What is your name?" : "What is your name?" + } + return `That's the end of the game, ${name}` +>`That's the end of the game, ${name}` : string +>name : string +} + +const looserSyncFactory: SequenceFactory = function* (){ +>looserSyncFactory : SequenceFactory +>function* (){ let name = ""; while(!name){ name = yield "What is your name?" } return `That's the end of the game, ${name}`} : () => Generator + + let name = ""; +>name : string +>"" : "" + + while(!name){ +>!name : boolean +>name : string + + name = yield "What is your name?" +>name = yield "What is your name?" : string +>name : string +>yield "What is your name?" : string +>"What is your name?" : "What is your name?" + } + return `That's the end of the game, ${name}` +>`That's the end of the game, ${name}` : string +>name : string +} + +const looserAsyncFactory: SequenceFactory = async function* (){ +>looserAsyncFactory : SequenceFactory +>async function* (){ let name = ""; while(!name){ name = yield "What is your name?" } return `That's the end of the game, ${name}`} : () => AsyncGenerator + + let name = ""; +>name : string +>"" : "" + + while(!name){ +>!name : boolean +>name : string + + name = yield "What is your name?" +>name = yield "What is your name?" : string +>name : string +>yield "What is your name?" : string +>"What is your name?" : "What is your name?" + } + return `That's the end of the game, ${name}` +>`That's the end of the game, ${name}` : string +>name : string +} diff --git a/tests/cases/compiler/typeOfYieldWithUnionInContextualReturnType.ts b/tests/cases/compiler/typeOfYieldWithUnionInContextualReturnType.ts new file mode 100644 index 0000000000000..f444a5461a46b --- /dev/null +++ b/tests/cases/compiler/typeOfYieldWithUnionInContextualReturnType.ts @@ -0,0 +1,40 @@ +// @target: esnext +// https://github.com/microsoft/TypeScript/issues/42439 + +type SyncSequenceFactory = () => Generator; + +type AsyncSequenceFactory = () => AsyncGenerator; + +type SequenceFactory = SyncSequenceFactory | AsyncSequenceFactory + +const syncFactory: SyncSequenceFactory = function* (){ + let name = ""; + while(!name){ + name = yield "What is your name?" + } + return `That's the end of the game, ${name}` +} + +const asyncFactory: AsyncSequenceFactory = async function* (){ + let name = ""; + while(!name){ + name = yield "What is your name?" + } + return `That's the end of the game, ${name}` +} + +const looserSyncFactory: SequenceFactory = function* (){ + let name = ""; + while(!name){ + name = yield "What is your name?" + } + return `That's the end of the game, ${name}` +} + +const looserAsyncFactory: SequenceFactory = async function* (){ + let name = ""; + while(!name){ + name = yield "What is your name?" + } + return `That's the end of the game, ${name}` +} \ No newline at end of file