From f1697dac827c65497130821890273166e91280c4 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Wed, 25 Oct 2023 18:02:32 -0400 Subject: [PATCH 1/2] Consider all constituents of contextual return type when resolving type of 'yield' --- src/compiler/checker.ts | 27 ++- src/compiler/debug.ts | 201 +++++++++--------- .../reference/contextualTypeOnYield1.types | 4 +- .../reference/contextualTypeOnYield2.types | 2 +- .../generatorYieldContextualType.types | 6 +- ...eOfYieldWithUnionInContextualReturnType.js | 73 +++++++ ...eldWithUnionInContextualReturnType.symbols | 85 ++++++++ ...YieldWithUnionInContextualReturnType.types | 105 +++++++++ ...eOfYieldWithUnionInContextualReturnType.ts | 40 ++++ 9 files changed, 433 insertions(+), 110 deletions(-) create mode 100644 tests/baselines/reference/typeOfYieldWithUnionInContextualReturnType.js create mode 100644 tests/baselines/reference/typeOfYieldWithUnionInContextualReturnType.symbols create mode 100644 tests/baselines/reference/typeOfYieldWithUnionInContextualReturnType.types create mode 100644 tests/cases/compiler/typeOfYieldWithUnionInContextualReturnType.ts 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/src/compiler/debug.ts b/src/compiler/debug.ts index 8912ffee391f1..e033ef631aa12 100644 --- a/src/compiler/debug.ts +++ b/src/compiler/debug.ts @@ -108,6 +108,8 @@ export interface LoggingHost { log(level: LogLevel, s: string): void; } +const kDebugDescription = typeof globalThis === "object" ? globalThis.Symbol.for("debug.description") : "__tsDebuggerDisplay"; + /** @internal */ export namespace Debug { /* eslint-disable prefer-const */ @@ -508,28 +510,29 @@ export namespace Debug { let flowNodeProto: FlowNodeBase | undefined; + function flowNodeDebugDescription(this: FlowNodeBase) { + const flowHeader = this.flags & FlowFlags.Start ? "FlowStart" : + this.flags & FlowFlags.BranchLabel ? "FlowBranchLabel" : + this.flags & FlowFlags.LoopLabel ? "FlowLoopLabel" : + this.flags & FlowFlags.Assignment ? "FlowAssignment" : + this.flags & FlowFlags.TrueCondition ? "FlowTrueCondition" : + this.flags & FlowFlags.FalseCondition ? "FlowFalseCondition" : + this.flags & FlowFlags.SwitchClause ? "FlowSwitchClause" : + this.flags & FlowFlags.ArrayMutation ? "FlowArrayMutation" : + this.flags & FlowFlags.Call ? "FlowCall" : + this.flags & FlowFlags.ReduceLabel ? "FlowReduceLabel" : + this.flags & FlowFlags.Unreachable ? "FlowUnreachable" : + "UnknownFlow"; + const remainingFlags = this.flags & ~(FlowFlags.Referenced - 1); + return `${flowHeader}${remainingFlags ? ` (${formatFlowFlags(remainingFlags)})` : ""}`; + } + function attachFlowNodeDebugInfoWorker(flowNode: FlowNodeBase) { if (!("__debugFlowFlags" in flowNode)) { // eslint-disable-line local/no-in-operator Object.defineProperties(flowNode, { + [kDebugDescription]: { value: flowNodeDebugDescription }, // for use with vscode-js-debug's new customDescriptionGenerator in launch.json - __tsDebuggerDisplay: { - value(this: FlowNodeBase) { - const flowHeader = this.flags & FlowFlags.Start ? "FlowStart" : - this.flags & FlowFlags.BranchLabel ? "FlowBranchLabel" : - this.flags & FlowFlags.LoopLabel ? "FlowLoopLabel" : - this.flags & FlowFlags.Assignment ? "FlowAssignment" : - this.flags & FlowFlags.TrueCondition ? "FlowTrueCondition" : - this.flags & FlowFlags.FalseCondition ? "FlowFalseCondition" : - this.flags & FlowFlags.SwitchClause ? "FlowSwitchClause" : - this.flags & FlowFlags.ArrayMutation ? "FlowArrayMutation" : - this.flags & FlowFlags.Call ? "FlowCall" : - this.flags & FlowFlags.ReduceLabel ? "FlowReduceLabel" : - this.flags & FlowFlags.Unreachable ? "FlowUnreachable" : - "UnknownFlow"; - const remainingFlags = this.flags & ~(FlowFlags.Referenced - 1); - return `${flowHeader}${remainingFlags ? ` (${formatFlowFlags(remainingFlags)})` : ""}`; - }, - }, + // __tsDebuggerDisplay: { value: flowNodeDebugDescription, }, __debugFlowFlags: { get(this: FlowNodeBase) { return formatEnum(this.flags, (ts as any).FlowFlags, /*isFlags*/ true); @@ -611,17 +614,18 @@ export namespace Debug { const weakTypeTextMap = new WeakMap(); const weakNodeTextMap = new WeakMap(); + function symbolDebugDescription(this: Symbol) { + const symbolHeader = this.flags & SymbolFlags.Transient ? "TransientSymbol" : + "Symbol"; + const remainingSymbolFlags = this.flags & ~SymbolFlags.Transient; + return `${symbolHeader} '${symbolName(this)}'${remainingSymbolFlags ? ` (${formatSymbolFlags(remainingSymbolFlags)})` : ""}`; + } + // Add additional properties in debug mode to assist with debugging. Object.defineProperties(objectAllocator.getSymbolConstructor().prototype, { + [kDebugDescription]: { value: symbolDebugDescription }, // for use with vscode-js-debug's new customDescriptionGenerator in launch.json - __tsDebuggerDisplay: { - value(this: Symbol) { - const symbolHeader = this.flags & SymbolFlags.Transient ? "TransientSymbol" : - "Symbol"; - const remainingSymbolFlags = this.flags & ~SymbolFlags.Transient; - return `${symbolHeader} '${symbolName(this)}'${remainingSymbolFlags ? ` (${formatSymbolFlags(remainingSymbolFlags)})` : ""}`; - }, - }, + // __tsDebuggerDisplay: { value: symbolDebugDescription, }, __debugFlags: { get(this: Symbol) { return formatSymbolFlags(this.flags); @@ -629,37 +633,39 @@ export namespace Debug { }, }); + function typeDebugDescription(this: Type) { + const typeHeader = this.flags & TypeFlags.Intrinsic ? `IntrinsicType ${(this as IntrinsicType).intrinsicName}${(this as IntrinsicType).debugIntrinsicName ? ` (${(this as IntrinsicType).debugIntrinsicName})` : ""}` : + this.flags & TypeFlags.Nullable ? "NullableType" : + this.flags & TypeFlags.StringOrNumberLiteral ? `LiteralType ${JSON.stringify((this as LiteralType).value)}` : + this.flags & TypeFlags.BigIntLiteral ? `LiteralType ${(this as BigIntLiteralType).value.negative ? "-" : ""}${(this as BigIntLiteralType).value.base10Value}n` : + this.flags & TypeFlags.UniqueESSymbol ? "UniqueESSymbolType" : + this.flags & TypeFlags.Enum ? "EnumType" : + this.flags & TypeFlags.Union ? "UnionType" : + this.flags & TypeFlags.Intersection ? "IntersectionType" : + this.flags & TypeFlags.Index ? "IndexType" : + this.flags & TypeFlags.IndexedAccess ? "IndexedAccessType" : + this.flags & TypeFlags.Conditional ? "ConditionalType" : + this.flags & TypeFlags.Substitution ? "SubstitutionType" : + this.flags & TypeFlags.TypeParameter ? "TypeParameter" : + this.flags & TypeFlags.Object ? + (this as ObjectType).objectFlags & ObjectFlags.ClassOrInterface ? "InterfaceType" : + (this as ObjectType).objectFlags & ObjectFlags.Reference ? "TypeReference" : + (this as ObjectType).objectFlags & ObjectFlags.Tuple ? "TupleType" : + (this as ObjectType).objectFlags & ObjectFlags.Anonymous ? "AnonymousType" : + (this as ObjectType).objectFlags & ObjectFlags.Mapped ? "MappedType" : + (this as ObjectType).objectFlags & ObjectFlags.ReverseMapped ? "ReverseMappedType" : + (this as ObjectType).objectFlags & ObjectFlags.EvolvingArray ? "EvolvingArrayType" : + "ObjectType" : + "Type"; + const remainingObjectFlags = this.flags & TypeFlags.Object ? (this as ObjectType).objectFlags & ~ObjectFlags.ObjectTypeKindMask : 0; + return `${typeHeader}${this.symbol ? ` '${symbolName(this.symbol)}'` : ""}${remainingObjectFlags ? ` (${formatObjectFlags(remainingObjectFlags)})` : ""}`; + } + Object.defineProperties(objectAllocator.getTypeConstructor().prototype, { + [kDebugDescription]: { value: typeDebugDescription }, // for use with vscode-js-debug's new customDescriptionGenerator in launch.json - __tsDebuggerDisplay: { - value(this: Type) { - const typeHeader = this.flags & TypeFlags.Intrinsic ? `IntrinsicType ${(this as IntrinsicType).intrinsicName}${(this as IntrinsicType).debugIntrinsicName ? ` (${(this as IntrinsicType).debugIntrinsicName})` : ""}` : - this.flags & TypeFlags.Nullable ? "NullableType" : - this.flags & TypeFlags.StringOrNumberLiteral ? `LiteralType ${JSON.stringify((this as LiteralType).value)}` : - this.flags & TypeFlags.BigIntLiteral ? `LiteralType ${(this as BigIntLiteralType).value.negative ? "-" : ""}${(this as BigIntLiteralType).value.base10Value}n` : - this.flags & TypeFlags.UniqueESSymbol ? "UniqueESSymbolType" : - this.flags & TypeFlags.Enum ? "EnumType" : - this.flags & TypeFlags.Union ? "UnionType" : - this.flags & TypeFlags.Intersection ? "IntersectionType" : - this.flags & TypeFlags.Index ? "IndexType" : - this.flags & TypeFlags.IndexedAccess ? "IndexedAccessType" : - this.flags & TypeFlags.Conditional ? "ConditionalType" : - this.flags & TypeFlags.Substitution ? "SubstitutionType" : - this.flags & TypeFlags.TypeParameter ? "TypeParameter" : - this.flags & TypeFlags.Object ? - (this as ObjectType).objectFlags & ObjectFlags.ClassOrInterface ? "InterfaceType" : - (this as ObjectType).objectFlags & ObjectFlags.Reference ? "TypeReference" : - (this as ObjectType).objectFlags & ObjectFlags.Tuple ? "TupleType" : - (this as ObjectType).objectFlags & ObjectFlags.Anonymous ? "AnonymousType" : - (this as ObjectType).objectFlags & ObjectFlags.Mapped ? "MappedType" : - (this as ObjectType).objectFlags & ObjectFlags.ReverseMapped ? "ReverseMappedType" : - (this as ObjectType).objectFlags & ObjectFlags.EvolvingArray ? "EvolvingArrayType" : - "ObjectType" : - "Type"; - const remainingObjectFlags = this.flags & TypeFlags.Object ? (this as ObjectType).objectFlags & ~ObjectFlags.ObjectTypeKindMask : 0; - return `${typeHeader}${this.symbol ? ` '${symbolName(this.symbol)}'` : ""}${remainingObjectFlags ? ` (${formatObjectFlags(remainingObjectFlags)})` : ""}`; - }, - }, + // __tsDebuggerDisplay: { value: typeDebugDescription }, + __debugFlags: { get(this: Type) { return formatTypeFlags(this.flags); @@ -703,52 +709,53 @@ export namespace Debug { objectAllocator.getSourceFileConstructor(), ]; + function nodeDebugDescription(this: Node) { + const nodeHeader = isGeneratedIdentifier(this) ? "GeneratedIdentifier" : + isIdentifier(this) ? `Identifier '${idText(this)}'` : + isPrivateIdentifier(this) ? `PrivateIdentifier '${idText(this)}'` : + isStringLiteral(this) ? `StringLiteral ${JSON.stringify(this.text.length < 10 ? this.text : this.text.slice(10) + "...")}` : + isNumericLiteral(this) ? `NumericLiteral ${this.text}` : + isBigIntLiteral(this) ? `BigIntLiteral ${this.text}n` : + isTypeParameterDeclaration(this) ? "TypeParameterDeclaration" : + isParameter(this) ? "ParameterDeclaration" : + isConstructorDeclaration(this) ? "ConstructorDeclaration" : + isGetAccessorDeclaration(this) ? "GetAccessorDeclaration" : + isSetAccessorDeclaration(this) ? "SetAccessorDeclaration" : + isCallSignatureDeclaration(this) ? "CallSignatureDeclaration" : + isConstructSignatureDeclaration(this) ? "ConstructSignatureDeclaration" : + isIndexSignatureDeclaration(this) ? "IndexSignatureDeclaration" : + isTypePredicateNode(this) ? "TypePredicateNode" : + isTypeReferenceNode(this) ? "TypeReferenceNode" : + isFunctionTypeNode(this) ? "FunctionTypeNode" : + isConstructorTypeNode(this) ? "ConstructorTypeNode" : + isTypeQueryNode(this) ? "TypeQueryNode" : + isTypeLiteralNode(this) ? "TypeLiteralNode" : + isArrayTypeNode(this) ? "ArrayTypeNode" : + isTupleTypeNode(this) ? "TupleTypeNode" : + isOptionalTypeNode(this) ? "OptionalTypeNode" : + isRestTypeNode(this) ? "RestTypeNode" : + isUnionTypeNode(this) ? "UnionTypeNode" : + isIntersectionTypeNode(this) ? "IntersectionTypeNode" : + isConditionalTypeNode(this) ? "ConditionalTypeNode" : + isInferTypeNode(this) ? "InferTypeNode" : + isParenthesizedTypeNode(this) ? "ParenthesizedTypeNode" : + isThisTypeNode(this) ? "ThisTypeNode" : + isTypeOperatorNode(this) ? "TypeOperatorNode" : + isIndexedAccessTypeNode(this) ? "IndexedAccessTypeNode" : + isMappedTypeNode(this) ? "MappedTypeNode" : + isLiteralTypeNode(this) ? "LiteralTypeNode" : + isNamedTupleMember(this) ? "NamedTupleMember" : + isImportTypeNode(this) ? "ImportTypeNode" : + formatSyntaxKind(this.kind); + return `${nodeHeader}${this.flags ? ` (${formatNodeFlags(this.flags)})` : ""}`; + } + for (const ctor of nodeConstructors) { if (!hasProperty(ctor.prototype, "__debugKind")) { Object.defineProperties(ctor.prototype, { + [kDebugDescription]: { value: nodeDebugDescription }, // for use with vscode-js-debug's new customDescriptionGenerator in launch.json - __tsDebuggerDisplay: { - value(this: Node) { - const nodeHeader = isGeneratedIdentifier(this) ? "GeneratedIdentifier" : - isIdentifier(this) ? `Identifier '${idText(this)}'` : - isPrivateIdentifier(this) ? `PrivateIdentifier '${idText(this)}'` : - isStringLiteral(this) ? `StringLiteral ${JSON.stringify(this.text.length < 10 ? this.text : this.text.slice(10) + "...")}` : - isNumericLiteral(this) ? `NumericLiteral ${this.text}` : - isBigIntLiteral(this) ? `BigIntLiteral ${this.text}n` : - isTypeParameterDeclaration(this) ? "TypeParameterDeclaration" : - isParameter(this) ? "ParameterDeclaration" : - isConstructorDeclaration(this) ? "ConstructorDeclaration" : - isGetAccessorDeclaration(this) ? "GetAccessorDeclaration" : - isSetAccessorDeclaration(this) ? "SetAccessorDeclaration" : - isCallSignatureDeclaration(this) ? "CallSignatureDeclaration" : - isConstructSignatureDeclaration(this) ? "ConstructSignatureDeclaration" : - isIndexSignatureDeclaration(this) ? "IndexSignatureDeclaration" : - isTypePredicateNode(this) ? "TypePredicateNode" : - isTypeReferenceNode(this) ? "TypeReferenceNode" : - isFunctionTypeNode(this) ? "FunctionTypeNode" : - isConstructorTypeNode(this) ? "ConstructorTypeNode" : - isTypeQueryNode(this) ? "TypeQueryNode" : - isTypeLiteralNode(this) ? "TypeLiteralNode" : - isArrayTypeNode(this) ? "ArrayTypeNode" : - isTupleTypeNode(this) ? "TupleTypeNode" : - isOptionalTypeNode(this) ? "OptionalTypeNode" : - isRestTypeNode(this) ? "RestTypeNode" : - isUnionTypeNode(this) ? "UnionTypeNode" : - isIntersectionTypeNode(this) ? "IntersectionTypeNode" : - isConditionalTypeNode(this) ? "ConditionalTypeNode" : - isInferTypeNode(this) ? "InferTypeNode" : - isParenthesizedTypeNode(this) ? "ParenthesizedTypeNode" : - isThisTypeNode(this) ? "ThisTypeNode" : - isTypeOperatorNode(this) ? "TypeOperatorNode" : - isIndexedAccessTypeNode(this) ? "IndexedAccessTypeNode" : - isMappedTypeNode(this) ? "MappedTypeNode" : - isLiteralTypeNode(this) ? "LiteralTypeNode" : - isNamedTupleMember(this) ? "NamedTupleMember" : - isImportTypeNode(this) ? "ImportTypeNode" : - formatSyntaxKind(this.kind); - return `${nodeHeader}${this.flags ? ` (${formatNodeFlags(this.flags)})` : ""}`; - }, - }, + // __tsDebuggerDisplay: { value: nodeDebugDescription, }, __debugKind: { get(this: Node) { return formatSyntaxKind(this.kind); 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 From e3b1f963ed7bbd8b303a6f700c96e6b2ec834ab7 Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Wed, 25 Oct 2023 18:02:32 -0400 Subject: [PATCH 2/2] Consider all constituents of contextual return type when resolving type of 'yield' --- src/compiler/checker.ts | 27 +++-- .../reference/contextualTypeOnYield1.types | 4 +- .../reference/contextualTypeOnYield2.types | 2 +- .../generatorYieldContextualType.types | 6 +- ...eOfYieldWithUnionInContextualReturnType.js | 73 ++++++++++++ ...eldWithUnionInContextualReturnType.symbols | 85 ++++++++++++++ ...YieldWithUnionInContextualReturnType.types | 105 ++++++++++++++++++ ...eOfYieldWithUnionInContextualReturnType.ts | 40 +++++++ 8 files changed, 329 insertions(+), 13 deletions(-) create mode 100644 tests/baselines/reference/typeOfYieldWithUnionInContextualReturnType.js create mode 100644 tests/baselines/reference/typeOfYieldWithUnionInContextualReturnType.symbols create mode 100644 tests/baselines/reference/typeOfYieldWithUnionInContextualReturnType.types create mode 100644 tests/cases/compiler/typeOfYieldWithUnionInContextualReturnType.ts 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