diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 85c6b695063d7..9573b1dd72727 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -845,6 +845,7 @@ namespace ts { const symbolLinks: SymbolLinks[] = []; const nodeLinks: NodeLinks[] = []; const flowLoopCaches: Map[] = []; + const flowLoopContainingUnionCache: (UnionType | undefined)[] = []; const flowLoopNodes: FlowNode[] = []; const flowLoopKeys: string[] = []; const flowLoopTypes: Type[][] = []; @@ -19043,6 +19044,7 @@ namespace ts { let key: string | undefined; let keySet = false; let flowDepth = 0; + let containingUnion: UnionType | undefined; if (flowAnalysisDisabled) { return errorType; } @@ -19071,7 +19073,14 @@ namespace ts { return key = getFlowCacheKey(reference, declaredType, initialType, flowContainer); } + function captureContainingUnion(type: FlowType, flags?: FlowFlags) { + if ((flags === undefined || (flags & FlowFlags.BranchLabel) === 0) && !isIncomplete(type) && type.flags & TypeFlags.Union) { + containingUnion = type as UnionType; + } + } + function getTypeAtFlowNode(flow: FlowNode): FlowType { + containingUnion = undefined; if (flowDepth === 2000) { // We have made 2000 recursive invocations. To avoid overflowing the call stack we report an error // and disable further control flow analysis in the containing function or module body. @@ -19089,6 +19098,7 @@ namespace ts { for (let i = sharedFlowStart; i < sharedFlowCount; i++) { if (sharedFlowNodes[i] === flow) { flowDepth--; + captureContainingUnion(sharedFlowTypes[i]); return sharedFlowTypes[i]; } } @@ -19166,6 +19176,7 @@ namespace ts { sharedFlowTypes[sharedFlowCount] = type; sharedFlowCount++; } + captureContainingUnion(type, flags); flowDepth--; return type; } @@ -19352,6 +19363,7 @@ namespace ts { function getTypeAtFlowBranchLabel(flow: FlowLabel): FlowType { const antecedentTypes: Type[] = []; + const containedUnions: Type[] = []; let subtypeReduction = false; let seenIncomplete = false; let bypassFlow: FlowSwitchClause | undefined; @@ -19374,9 +19386,11 @@ namespace ts { // are the same), there is no reason to process more antecedents since the only // possible outcome is subtypes that will be removed in the final union type anyway. if (type === declaredType && declaredType === initialType) { + containingUnion = undefined; return type; } pushIfUnique(antecedentTypes, type); + pushIfUnique(containedUnions, containingUnion || type); // If an antecedent type is not a subset of the declared type, we need to perform // subtype reduction. This happens when a "foreign" type is injected into the control // flow using the instanceof operator or a user defined type predicate. @@ -19395,9 +19409,11 @@ namespace ts { // the risk of circularities, we only want to perform them when they make a difference. if (!contains(antecedentTypes, type) && !isExhaustiveSwitchStatement(bypassFlow.switchStatement)) { if (type === declaredType && declaredType === initialType) { + containingUnion = undefined; return type; } antecedentTypes.push(type); + pushIfUnique(containedUnions, containingUnion || type); if (!isTypeSubsetOf(type, declaredType)) { subtypeReduction = true; } @@ -19406,6 +19422,13 @@ namespace ts { } } } + containingUnion = undefined; + captureContainingUnion( + createFlowType( + getUnionOrEvolvingArrayType(containedUnions, subtypeReduction ? UnionReduction.Subtype : UnionReduction.Literal), + seenIncomplete + ) + ); return createFlowType(getUnionOrEvolvingArrayType(antecedentTypes, subtypeReduction ? UnionReduction.Subtype : UnionReduction.Literal), seenIncomplete); } @@ -19421,6 +19444,7 @@ namespace ts { } const cached = cache.get(key); if (cached) { + containingUnion = flowLoopContainingUnionCache[id]; return cached; } // If this flow loop junction and reference are already being processed, return @@ -19441,12 +19465,14 @@ namespace ts { const antecedentTypes: Type[] = []; let subtypeReduction = false; let firstAntecedentType: FlowType | undefined; + let unionContainedAtTop: UnionType | undefined; for (const antecedent of flow.antecedents!) { let flowType; if (!firstAntecedentType) { // The first antecedent of a loop junction is always the non-looping control // flow path that leads to the top. flowType = firstAntecedentType = getTypeAtFlowNode(antecedent); + unionContainedAtTop = containingUnion; } else { // All but the first antecedent are the looping control flow paths that lead @@ -19465,6 +19491,7 @@ namespace ts { // the resulting type and bail out. const cached = cache.get(key); if (cached) { + containingUnion = flowLoopContainingUnionCache[id]; return cached; } } @@ -19487,8 +19514,10 @@ namespace ts { // is incomplete. const result = getUnionOrEvolvingArrayType(antecedentTypes, subtypeReduction ? UnionReduction.Subtype : UnionReduction.Literal); if (isIncomplete(firstAntecedentType!)) { + containingUnion = undefined; return createFlowType(result, /*incomplete*/ true); } + containingUnion = flowLoopContainingUnionCache[id] = unionContainedAtTop; cache.set(key, result); return result; } @@ -19521,7 +19550,7 @@ namespace ts { if (strictNullChecks && assumeTrue && optionalChainContainsReference(expr, reference)) { type = getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull); } - if (isMatchingReferenceDiscriminant(expr, declaredType)) { + if (isMatchingReferenceDiscriminant(expr, containingUnion || declaredType)) { return narrowTypeByDiscriminant(type, expr, t => getTypeWithFacts(t, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy)); } if (containsMatchingReferenceDiscriminant(reference, expr)) { @@ -19580,10 +19609,10 @@ namespace ts { type = narrowTypeByOptionalChainContainment(type, operator, left, assumeTrue); } } - if (isMatchingReferenceDiscriminant(left, declaredType)) { + if (isMatchingReferenceDiscriminant(left, containingUnion || declaredType)) { return narrowTypeByDiscriminant(type, left, t => narrowTypeByEquality(t, operator, right, assumeTrue)); } - if (isMatchingReferenceDiscriminant(right, declaredType)) { + if (isMatchingReferenceDiscriminant(right, containingUnion || declaredType)) { return narrowTypeByDiscriminant(type, right, t => narrowTypeByEquality(t, operator, left, assumeTrue)); } if (containsMatchingReferenceDiscriminant(reference, left) || containsMatchingReferenceDiscriminant(reference, right)) { @@ -19988,7 +20017,7 @@ namespace ts { if (isMatchingReference(reference, expr)) { return getTypeWithFacts(type, assumePresent ? TypeFacts.NEUndefinedOrNull : TypeFacts.EQUndefinedOrNull); } - if (isMatchingReferenceDiscriminant(expr, declaredType)) { + if (isMatchingReferenceDiscriminant(expr, containingUnion || declaredType)) { return narrowTypeByDiscriminant(type, expr, t => getTypeWithFacts(t, assumePresent ? TypeFacts.NEUndefinedOrNull : TypeFacts.EQUndefinedOrNull)); } if (containsMatchingReferenceDiscriminant(reference, expr)) { diff --git a/tests/baselines/reference/discriminantsAndTypePredicates.errors.txt b/tests/baselines/reference/discriminantsAndTypePredicates.errors.txt new file mode 100644 index 0000000000000..6c06f976b62e5 --- /dev/null +++ b/tests/baselines/reference/discriminantsAndTypePredicates.errors.txt @@ -0,0 +1,377 @@ +tests/cases/compiler/discriminantsAndTypePredicates.ts(238,15): error TS2551: Property 'kind2' does not exist on type '{ kind1: "a"; a: 1; } | { kind1: "b"; b: 2; }'. Did you mean 'kind1'? + Property 'kind2' does not exist on type '{ kind1: "a"; a: 1; }'. +tests/cases/compiler/discriminantsAndTypePredicates.ts(239,15): error TS2339: Property 'c' does not exist on type '{ kind1: "a"; a: 1; } | { kind1: "b"; b: 2; }'. + Property 'c' does not exist on type '{ kind1: "a"; a: 1; }'. +tests/cases/compiler/discriminantsAndTypePredicates.ts(252,15): error TS2339: Property 'a' does not exist on type '{ kind1: string; a?: number; b?: number; } | { kind1: "a"; a: 1; } | { kind1: "b"; b: 2; }'. + Property 'a' does not exist on type '{ kind1: "b"; b: 2; }'. +tests/cases/compiler/discriminantsAndTypePredicates.ts(264,11): error TS2339: Property 'kind1' does not exist on type 'unknown'. +tests/cases/compiler/discriminantsAndTypePredicates.ts(265,11): error TS2339: Property 'a' does not exist on type 'unknown'. +tests/cases/compiler/discriminantsAndTypePredicates.ts(273,11): error TS2339: Property 'kind2' does not exist on type 'unknown'. +tests/cases/compiler/discriminantsAndTypePredicates.ts(274,11): error TS2339: Property 'c' does not exist on type 'unknown'. +tests/cases/compiler/discriminantsAndTypePredicates.ts(302,15): error TS2551: Property 'kind2' does not exist on type '{ kind1: "a"; a: 1; } | { kind1: "b"; b: 2; }'. Did you mean 'kind1'? + Property 'kind2' does not exist on type '{ kind1: "a"; a: 1; }'. +tests/cases/compiler/discriminantsAndTypePredicates.ts(303,15): error TS2339: Property 'c' does not exist on type '{ kind1: "a"; a: 1; } | { kind1: "b"; b: 2; }'. + Property 'c' does not exist on type '{ kind1: "a"; a: 1; }'. +tests/cases/compiler/discriminantsAndTypePredicates.ts(306,11): error TS2339: Property 'kind1' does not exist on type 'unknown'. +tests/cases/compiler/discriminantsAndTypePredicates.ts(307,11): error TS2339: Property 'a' does not exist on type 'unknown'. + + +==== tests/cases/compiler/discriminantsAndTypePredicates.ts (11 errors) ==== + // Repro from #10145 + + interface A { type: 'A' } + interface B { type: 'B' } + + function isA(x: A | B): x is A { return x.type === 'A'; } + function isB(x: A | B): x is B { return x.type === 'B'; } + + function foo1(x: A | B): any { + x; // A | B + if (isA(x)) { + return x; // A + } + x; // B + if (isB(x)) { + return x; // B + } + x; // never + } + + function foo2(x: A | B): any { + x; // A | B + if (x.type === 'A') { + return x; // A + } + x; // B + if (x.type === 'B') { + return x; // B + } + x; // never + } + + // Repro from #30557 + + interface TypeA { + Name: "TypeA"; + Value1: "Cool stuff!"; + } + + interface TypeB { + Name: "TypeB"; + Value2: 0; + } + + type Type = TypeA | TypeB; + + declare function isType(x: unknown): x is Type; + + function WorksProperly(data: Type) { + if (data.Name === "TypeA") { + // TypeA + const value1 = data.Value1; + } + } + + function DoesNotWork(data: unknown) { + if (isType(data)) { + if (data.Name === "TypeA") { + // TypeA + const value1 = data.Value1; + } + } + } + + function narrowToNever(data: Type): "Cool stuff!" | 0 { + if (data.Name === "TypeA") { + return data.Value1; + } + if (data.Name === "TypeB") { + return data.Value2; + } + return data; + } + + function narrowToNeverUnknown(data: unknown): "Cool stuff!" | 0 { + if (isType(data)) { + if (data.Name === "TypeA") { + return data.Value1; + } + if (data.Name === "TypeB") { + return data.Value2; + } + return data; + } + throw "error"; + } + + type Foo = { kind: "a", a: number } | { kind: "b", b: number }; + type Bar = { kind: "c", c: number } | { kind: "d", d: number }; + + declare function isFoo(x: unknown): x is Foo; + declare function isBar(x: unknown): x is Bar; + + function blah(x: unknown) { + if (isFoo(x)) { + if (x.kind === "a") { + let a = x.a; + } + else if (x.kind === "b") { + let b = x.b; + } + } + else if (isBar(x)) { + if (x.kind === "c") { + let c = x.c; + } + else if (x.kind === "d") { + let d = x.d; + } + } + x // unknown + } + + type PrimitiveUnion = number | string + type FooComplex = { kind: "a", a: number } | { kind: "b", b: number } | number; + type BarComplex = { kind: "c", c: number } | { kind: "d", d: number } | string; + + declare function isPrimitiveUnion(x: unknown): x is PrimitiveUnion; + declare function isFooComplex(x: unknown): x is FooComplex; + declare function isBarComplex(x: unknown): x is BarComplex; + declare function isZZYYComplex(x: unknown): x is { kind: "z"; zzz: string } | { kind: "y", yyy: number }; + + function earlyExitsAndStuff(x: unknown) { + if (!isFooComplex(x) && !isBarComplex(x)) { + if (isZZYYComplex(x)) { + if (x.kind !== "z") { + return x.yyy; + } + return x.zzz; + } + return; + } + if (!!isPrimitiveUnion(x)) { + return x; + } + if (!isZZYYComplex(x)) { + if (x.kind === "a") { + let a = x.a; + } + if (x.kind === "b") { + let b = x.b; + } + if (x.kind === "c") { + let c = x.c; + } + if (x.kind === "d") { + let d = x.d; + } + } + } + + function bluergh(x: unknown) { + if (isPrimitiveUnion(x)) { + let a: number | string = x; + return; + } + if (isFooComplex(x) && typeof x === "object") { + if (x.kind === "a") { + let a = x.a; + } + else if (x.kind === "b") { + let b = x.b; + } + } + if (isPrimitiveUnion(x)) { + let a: number | string = x; + } + if (isBarComplex(x) && typeof x === "object") { + if (x.kind === "c") { + let c = x.c; + } + else if (x.kind === "d") { + let d = x.d; + } + } + if (isPrimitiveUnion(x)) { + let a: number | string = x; + } + x // unknown + } + + type A1 = { x: number }; + type B1 = A1 & { kind: "B"; y: number }; + type C1 = A1 & { kind: "C"; z: number }; + + function isBorC(a: A1): a is B1 | C1 { + return (a as any).kind === "B" || (a as any).kind === "C"; + } + + function isB1(a: A1): a is B1 { + return (a as any).kind === "B"; + } + + function isC1(a: A1): a is C1 { + return (a as any).kind === "C"; + } + + function fn1(a: A1) { + if (isBorC(a)) { + if (a.kind === "B") { + a.y; + } + } + } + + function fn2(a: A1) { + if (!isB1(a)) { + return; + } + if (!isC1(a)) { + if (a.kind === "B") { + a.y; + } + return; + } + if (a.kind === "B") { + a.y; + } + } + + declare function isTypeObj(x: unknown): x is { kind1: string, a?: number, b?: number }; + declare function isTypeAB(x: unknown): x is { kind1: 'a', a: 1 } | { kind1: 'b', b: 2 }; + declare function isTypeCD(x: unknown): x is { kind2: 'c', c: 3 } | { kind2: 'd', d: 4 }; + + function testComposition1(x: unknown) { + if (isTypeAB(x)) { + if (isTypeCD(x)) { + if (x.kind1 === 'a') { + x.a; + } + if (x.kind2 === 'c') { + x.c; + } + } + if (x.kind1 === 'a') { + x.a; + } + if (x.kind2 === 'c') { + ~~~~~ +!!! error TS2551: Property 'kind2' does not exist on type '{ kind1: "a"; a: 1; } | { kind1: "b"; b: 2; }'. Did you mean 'kind1'? +!!! error TS2551: Property 'kind2' does not exist on type '{ kind1: "a"; a: 1; }'. + x.c; + ~ +!!! error TS2339: Property 'c' does not exist on type '{ kind1: "a"; a: 1; } | { kind1: "b"; b: 2; }'. +!!! error TS2339: Property 'c' does not exist on type '{ kind1: "a"; a: 1; }'. + } + } + } + + function testComposition2(x: unknown) { + if (isTypeObj(x)) { + if (isTypeAB(x)) { + if (x.kind1 === "a") { + x.a; + } + } + if (x.kind1 === "a") { + x.a; // Error + ~ +!!! error TS2339: Property 'a' does not exist on type '{ kind1: string; a?: number; b?: number; } | { kind1: "a"; a: 1; } | { kind1: "b"; b: 2; }'. +!!! error TS2339: Property 'a' does not exist on type '{ kind1: "b"; b: 2; }'. + } + } + } + + function testComposition3(x: unknown) { + if (isTypeAB(x)) { + if (x.kind1 === 'a') { + x.a; + } + return; + } + if (x.kind1 === 'a') { + ~~~~~ +!!! error TS2339: Property 'kind1' does not exist on type 'unknown'. + x.a; // Error + ~ +!!! error TS2339: Property 'a' does not exist on type 'unknown'. + } + if (isTypeCD(x)) { + if (x.kind2 === 'c') { + x.c; + } + return; + } + if (x.kind2 === 'c') { + ~~~~~ +!!! error TS2339: Property 'kind2' does not exist on type 'unknown'. + x.c; // Error + ~ +!!! error TS2339: Property 'c' does not exist on type 'unknown'. + } + if (isTypeAB(x)) { + if (isTypeCD(x)) { + if (x.kind1 === 'a') { + x.a; + } + if (x.kind2 === 'c') { + x.c; + } + } + } + } + + function looper(getter: () => unknown) { + let x = getter(); + while (isTypeAB(x)) { + if (isTypeCD(x)) { + if (x.kind1 === 'a') { + x.a; + } + if (x.kind2 === 'c') { + x.c; + } + } + if (x.kind1 === 'a') { + x.a; + } + if (x.kind2 === 'c') { + ~~~~~ +!!! error TS2551: Property 'kind2' does not exist on type '{ kind1: "a"; a: 1; } | { kind1: "b"; b: 2; }'. Did you mean 'kind1'? +!!! error TS2551: Property 'kind2' does not exist on type '{ kind1: "a"; a: 1; }'. + x.c; // Error + ~ +!!! error TS2339: Property 'c' does not exist on type '{ kind1: "a"; a: 1; } | { kind1: "b"; b: 2; }'. +!!! error TS2339: Property 'c' does not exist on type '{ kind1: "a"; a: 1; }'. + } + } + if (x.kind1 === 'a') { + ~~~~~ +!!! error TS2339: Property 'kind1' does not exist on type 'unknown'. + x.a; // error + ~ +!!! error TS2339: Property 'a' does not exist on type 'unknown'. + } + } + + interface Success { + success: true; + response: object; + } + + interface Error { + success: false; + error: object; + } + + function request(): Success | Error { + return null as any; + } + + // This does not work: + let r + r = request(); + if (r.success) { + r.response; + } + \ No newline at end of file diff --git a/tests/baselines/reference/discriminantsAndTypePredicates.js b/tests/baselines/reference/discriminantsAndTypePredicates.js index 66cfe056ab711..68c7e3207589f 100644 --- a/tests/baselines/reference/discriminantsAndTypePredicates.js +++ b/tests/baselines/reference/discriminantsAndTypePredicates.js @@ -29,7 +29,307 @@ function foo2(x: A | B): any { return x; // B } x; // never -} +} + +// Repro from #30557 + +interface TypeA { + Name: "TypeA"; + Value1: "Cool stuff!"; +} + +interface TypeB { + Name: "TypeB"; + Value2: 0; +} + +type Type = TypeA | TypeB; + +declare function isType(x: unknown): x is Type; + +function WorksProperly(data: Type) { + if (data.Name === "TypeA") { + // TypeA + const value1 = data.Value1; + } +} + +function DoesNotWork(data: unknown) { + if (isType(data)) { + if (data.Name === "TypeA") { + // TypeA + const value1 = data.Value1; + } + } +} + +function narrowToNever(data: Type): "Cool stuff!" | 0 { + if (data.Name === "TypeA") { + return data.Value1; + } + if (data.Name === "TypeB") { + return data.Value2; + } + return data; +} + +function narrowToNeverUnknown(data: unknown): "Cool stuff!" | 0 { + if (isType(data)) { + if (data.Name === "TypeA") { + return data.Value1; + } + if (data.Name === "TypeB") { + return data.Value2; + } + return data; + } + throw "error"; +} + +type Foo = { kind: "a", a: number } | { kind: "b", b: number }; +type Bar = { kind: "c", c: number } | { kind: "d", d: number }; + +declare function isFoo(x: unknown): x is Foo; +declare function isBar(x: unknown): x is Bar; + +function blah(x: unknown) { + if (isFoo(x)) { + if (x.kind === "a") { + let a = x.a; + } + else if (x.kind === "b") { + let b = x.b; + } + } + else if (isBar(x)) { + if (x.kind === "c") { + let c = x.c; + } + else if (x.kind === "d") { + let d = x.d; + } + } + x // unknown +} + +type PrimitiveUnion = number | string +type FooComplex = { kind: "a", a: number } | { kind: "b", b: number } | number; +type BarComplex = { kind: "c", c: number } | { kind: "d", d: number } | string; + +declare function isPrimitiveUnion(x: unknown): x is PrimitiveUnion; +declare function isFooComplex(x: unknown): x is FooComplex; +declare function isBarComplex(x: unknown): x is BarComplex; +declare function isZZYYComplex(x: unknown): x is { kind: "z"; zzz: string } | { kind: "y", yyy: number }; + +function earlyExitsAndStuff(x: unknown) { + if (!isFooComplex(x) && !isBarComplex(x)) { + if (isZZYYComplex(x)) { + if (x.kind !== "z") { + return x.yyy; + } + return x.zzz; + } + return; + } + if (!!isPrimitiveUnion(x)) { + return x; + } + if (!isZZYYComplex(x)) { + if (x.kind === "a") { + let a = x.a; + } + if (x.kind === "b") { + let b = x.b; + } + if (x.kind === "c") { + let c = x.c; + } + if (x.kind === "d") { + let d = x.d; + } + } +} + +function bluergh(x: unknown) { + if (isPrimitiveUnion(x)) { + let a: number | string = x; + return; + } + if (isFooComplex(x) && typeof x === "object") { + if (x.kind === "a") { + let a = x.a; + } + else if (x.kind === "b") { + let b = x.b; + } + } + if (isPrimitiveUnion(x)) { + let a: number | string = x; + } + if (isBarComplex(x) && typeof x === "object") { + if (x.kind === "c") { + let c = x.c; + } + else if (x.kind === "d") { + let d = x.d; + } + } + if (isPrimitiveUnion(x)) { + let a: number | string = x; + } + x // unknown +} + +type A1 = { x: number }; +type B1 = A1 & { kind: "B"; y: number }; +type C1 = A1 & { kind: "C"; z: number }; + +function isBorC(a: A1): a is B1 | C1 { + return (a as any).kind === "B" || (a as any).kind === "C"; +} + +function isB1(a: A1): a is B1 { + return (a as any).kind === "B"; +} + +function isC1(a: A1): a is C1 { + return (a as any).kind === "C"; +} + +function fn1(a: A1) { + if (isBorC(a)) { + if (a.kind === "B") { + a.y; + } + } +} + +function fn2(a: A1) { + if (!isB1(a)) { + return; + } + if (!isC1(a)) { + if (a.kind === "B") { + a.y; + } + return; + } + if (a.kind === "B") { + a.y; + } +} + +declare function isTypeObj(x: unknown): x is { kind1: string, a?: number, b?: number }; +declare function isTypeAB(x: unknown): x is { kind1: 'a', a: 1 } | { kind1: 'b', b: 2 }; +declare function isTypeCD(x: unknown): x is { kind2: 'c', c: 3 } | { kind2: 'd', d: 4 }; + +function testComposition1(x: unknown) { + if (isTypeAB(x)) { + if (isTypeCD(x)) { + if (x.kind1 === 'a') { + x.a; + } + if (x.kind2 === 'c') { + x.c; + } + } + if (x.kind1 === 'a') { + x.a; + } + if (x.kind2 === 'c') { + x.c; + } + } +} + +function testComposition2(x: unknown) { + if (isTypeObj(x)) { + if (isTypeAB(x)) { + if (x.kind1 === "a") { + x.a; + } + } + if (x.kind1 === "a") { + x.a; // Error + } + } +} + +function testComposition3(x: unknown) { + if (isTypeAB(x)) { + if (x.kind1 === 'a') { + x.a; + } + return; + } + if (x.kind1 === 'a') { + x.a; // Error + } + if (isTypeCD(x)) { + if (x.kind2 === 'c') { + x.c; + } + return; + } + if (x.kind2 === 'c') { + x.c; // Error + } + if (isTypeAB(x)) { + if (isTypeCD(x)) { + if (x.kind1 === 'a') { + x.a; + } + if (x.kind2 === 'c') { + x.c; + } + } + } +} + +function looper(getter: () => unknown) { + let x = getter(); + while (isTypeAB(x)) { + if (isTypeCD(x)) { + if (x.kind1 === 'a') { + x.a; + } + if (x.kind2 === 'c') { + x.c; + } + } + if (x.kind1 === 'a') { + x.a; + } + if (x.kind2 === 'c') { + x.c; // Error + } + } + if (x.kind1 === 'a') { + x.a; // error + } +} + +interface Success { + success: true; + response: object; +} + +interface Error { + success: false; + error: object; +} + +function request(): Success | Error { + return null as any; +} + +// This does not work: +let r +r = request(); +if (r.success) { + r.response; +} + //// [discriminantsAndTypePredicates.js] // Repro from #10145 @@ -57,3 +357,235 @@ function foo2(x) { } x; // never } +function WorksProperly(data) { + if (data.Name === "TypeA") { + // TypeA + var value1 = data.Value1; + } +} +function DoesNotWork(data) { + if (isType(data)) { + if (data.Name === "TypeA") { + // TypeA + var value1 = data.Value1; + } + } +} +function narrowToNever(data) { + if (data.Name === "TypeA") { + return data.Value1; + } + if (data.Name === "TypeB") { + return data.Value2; + } + return data; +} +function narrowToNeverUnknown(data) { + if (isType(data)) { + if (data.Name === "TypeA") { + return data.Value1; + } + if (data.Name === "TypeB") { + return data.Value2; + } + return data; + } + throw "error"; +} +function blah(x) { + if (isFoo(x)) { + if (x.kind === "a") { + var a = x.a; + } + else if (x.kind === "b") { + var b = x.b; + } + } + else if (isBar(x)) { + if (x.kind === "c") { + var c = x.c; + } + else if (x.kind === "d") { + var d = x.d; + } + } + x; // unknown +} +function earlyExitsAndStuff(x) { + if (!isFooComplex(x) && !isBarComplex(x)) { + if (isZZYYComplex(x)) { + if (x.kind !== "z") { + return x.yyy; + } + return x.zzz; + } + return; + } + if (!!isPrimitiveUnion(x)) { + return x; + } + if (!isZZYYComplex(x)) { + if (x.kind === "a") { + var a = x.a; + } + if (x.kind === "b") { + var b = x.b; + } + if (x.kind === "c") { + var c = x.c; + } + if (x.kind === "d") { + var d = x.d; + } + } +} +function bluergh(x) { + if (isPrimitiveUnion(x)) { + var a = x; + return; + } + if (isFooComplex(x) && typeof x === "object") { + if (x.kind === "a") { + var a = x.a; + } + else if (x.kind === "b") { + var b = x.b; + } + } + if (isPrimitiveUnion(x)) { + var a = x; + } + if (isBarComplex(x) && typeof x === "object") { + if (x.kind === "c") { + var c = x.c; + } + else if (x.kind === "d") { + var d = x.d; + } + } + if (isPrimitiveUnion(x)) { + var a = x; + } + x; // unknown +} +function isBorC(a) { + return a.kind === "B" || a.kind === "C"; +} +function isB1(a) { + return a.kind === "B"; +} +function isC1(a) { + return a.kind === "C"; +} +function fn1(a) { + if (isBorC(a)) { + if (a.kind === "B") { + a.y; + } + } +} +function fn2(a) { + if (!isB1(a)) { + return; + } + if (!isC1(a)) { + if (a.kind === "B") { + a.y; + } + return; + } + if (a.kind === "B") { + a.y; + } +} +function testComposition1(x) { + if (isTypeAB(x)) { + if (isTypeCD(x)) { + if (x.kind1 === 'a') { + x.a; + } + if (x.kind2 === 'c') { + x.c; + } + } + if (x.kind1 === 'a') { + x.a; + } + if (x.kind2 === 'c') { + x.c; + } + } +} +function testComposition2(x) { + if (isTypeObj(x)) { + if (isTypeAB(x)) { + if (x.kind1 === "a") { + x.a; + } + } + if (x.kind1 === "a") { + x.a; // Error + } + } +} +function testComposition3(x) { + if (isTypeAB(x)) { + if (x.kind1 === 'a') { + x.a; + } + return; + } + if (x.kind1 === 'a') { + x.a; // Error + } + if (isTypeCD(x)) { + if (x.kind2 === 'c') { + x.c; + } + return; + } + if (x.kind2 === 'c') { + x.c; // Error + } + if (isTypeAB(x)) { + if (isTypeCD(x)) { + if (x.kind1 === 'a') { + x.a; + } + if (x.kind2 === 'c') { + x.c; + } + } + } +} +function looper(getter) { + var x = getter(); + while (isTypeAB(x)) { + if (isTypeCD(x)) { + if (x.kind1 === 'a') { + x.a; + } + if (x.kind2 === 'c') { + x.c; + } + } + if (x.kind1 === 'a') { + x.a; + } + if (x.kind2 === 'c') { + x.c; // Error + } + } + if (x.kind1 === 'a') { + x.a; // error + } +} +function request() { + return null; +} +// This does not work: +var r; +r = request(); +if (r.success) { + r.response; +} diff --git a/tests/baselines/reference/discriminantsAndTypePredicates.symbols b/tests/baselines/reference/discriminantsAndTypePredicates.symbols index 4f4128239364f..8ad74bc79a7da 100644 --- a/tests/baselines/reference/discriminantsAndTypePredicates.symbols +++ b/tests/baselines/reference/discriminantsAndTypePredicates.symbols @@ -92,3 +92,862 @@ function foo2(x: A | B): any { x; // never >x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 20, 14)) } + +// Repro from #30557 + +interface TypeA { +>TypeA : Symbol(TypeA, Decl(discriminantsAndTypePredicates.ts, 30, 1)) + + Name: "TypeA"; +>Name : Symbol(TypeA.Name, Decl(discriminantsAndTypePredicates.ts, 34, 17)) + + Value1: "Cool stuff!"; +>Value1 : Symbol(TypeA.Value1, Decl(discriminantsAndTypePredicates.ts, 35, 18)) +} + +interface TypeB { +>TypeB : Symbol(TypeB, Decl(discriminantsAndTypePredicates.ts, 37, 1)) + + Name: "TypeB"; +>Name : Symbol(TypeB.Name, Decl(discriminantsAndTypePredicates.ts, 39, 17)) + + Value2: 0; +>Value2 : Symbol(TypeB.Value2, Decl(discriminantsAndTypePredicates.ts, 40, 18)) +} + +type Type = TypeA | TypeB; +>Type : Symbol(Type, Decl(discriminantsAndTypePredicates.ts, 42, 1)) +>TypeA : Symbol(TypeA, Decl(discriminantsAndTypePredicates.ts, 30, 1)) +>TypeB : Symbol(TypeB, Decl(discriminantsAndTypePredicates.ts, 37, 1)) + +declare function isType(x: unknown): x is Type; +>isType : Symbol(isType, Decl(discriminantsAndTypePredicates.ts, 44, 26)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 46, 24)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 46, 24)) +>Type : Symbol(Type, Decl(discriminantsAndTypePredicates.ts, 42, 1)) + +function WorksProperly(data: Type) { +>WorksProperly : Symbol(WorksProperly, Decl(discriminantsAndTypePredicates.ts, 46, 47)) +>data : Symbol(data, Decl(discriminantsAndTypePredicates.ts, 48, 23)) +>Type : Symbol(Type, Decl(discriminantsAndTypePredicates.ts, 42, 1)) + + if (data.Name === "TypeA") { +>data.Name : Symbol(Name, Decl(discriminantsAndTypePredicates.ts, 34, 17), Decl(discriminantsAndTypePredicates.ts, 39, 17)) +>data : Symbol(data, Decl(discriminantsAndTypePredicates.ts, 48, 23)) +>Name : Symbol(Name, Decl(discriminantsAndTypePredicates.ts, 34, 17), Decl(discriminantsAndTypePredicates.ts, 39, 17)) + + // TypeA + const value1 = data.Value1; +>value1 : Symbol(value1, Decl(discriminantsAndTypePredicates.ts, 51, 13)) +>data.Value1 : Symbol(TypeA.Value1, Decl(discriminantsAndTypePredicates.ts, 35, 18)) +>data : Symbol(data, Decl(discriminantsAndTypePredicates.ts, 48, 23)) +>Value1 : Symbol(TypeA.Value1, Decl(discriminantsAndTypePredicates.ts, 35, 18)) + } +} + +function DoesNotWork(data: unknown) { +>DoesNotWork : Symbol(DoesNotWork, Decl(discriminantsAndTypePredicates.ts, 53, 1)) +>data : Symbol(data, Decl(discriminantsAndTypePredicates.ts, 55, 21)) + + if (isType(data)) { +>isType : Symbol(isType, Decl(discriminantsAndTypePredicates.ts, 44, 26)) +>data : Symbol(data, Decl(discriminantsAndTypePredicates.ts, 55, 21)) + + if (data.Name === "TypeA") { +>data.Name : Symbol(Name, Decl(discriminantsAndTypePredicates.ts, 34, 17), Decl(discriminantsAndTypePredicates.ts, 39, 17)) +>data : Symbol(data, Decl(discriminantsAndTypePredicates.ts, 55, 21)) +>Name : Symbol(Name, Decl(discriminantsAndTypePredicates.ts, 34, 17), Decl(discriminantsAndTypePredicates.ts, 39, 17)) + + // TypeA + const value1 = data.Value1; +>value1 : Symbol(value1, Decl(discriminantsAndTypePredicates.ts, 59, 17)) +>data.Value1 : Symbol(TypeA.Value1, Decl(discriminantsAndTypePredicates.ts, 35, 18)) +>data : Symbol(data, Decl(discriminantsAndTypePredicates.ts, 55, 21)) +>Value1 : Symbol(TypeA.Value1, Decl(discriminantsAndTypePredicates.ts, 35, 18)) + } + } +} + +function narrowToNever(data: Type): "Cool stuff!" | 0 { +>narrowToNever : Symbol(narrowToNever, Decl(discriminantsAndTypePredicates.ts, 62, 1)) +>data : Symbol(data, Decl(discriminantsAndTypePredicates.ts, 64, 23)) +>Type : Symbol(Type, Decl(discriminantsAndTypePredicates.ts, 42, 1)) + + if (data.Name === "TypeA") { +>data.Name : Symbol(Name, Decl(discriminantsAndTypePredicates.ts, 34, 17), Decl(discriminantsAndTypePredicates.ts, 39, 17)) +>data : Symbol(data, Decl(discriminantsAndTypePredicates.ts, 64, 23)) +>Name : Symbol(Name, Decl(discriminantsAndTypePredicates.ts, 34, 17), Decl(discriminantsAndTypePredicates.ts, 39, 17)) + + return data.Value1; +>data.Value1 : Symbol(TypeA.Value1, Decl(discriminantsAndTypePredicates.ts, 35, 18)) +>data : Symbol(data, Decl(discriminantsAndTypePredicates.ts, 64, 23)) +>Value1 : Symbol(TypeA.Value1, Decl(discriminantsAndTypePredicates.ts, 35, 18)) + } + if (data.Name === "TypeB") { +>data.Name : Symbol(TypeB.Name, Decl(discriminantsAndTypePredicates.ts, 39, 17)) +>data : Symbol(data, Decl(discriminantsAndTypePredicates.ts, 64, 23)) +>Name : Symbol(TypeB.Name, Decl(discriminantsAndTypePredicates.ts, 39, 17)) + + return data.Value2; +>data.Value2 : Symbol(TypeB.Value2, Decl(discriminantsAndTypePredicates.ts, 40, 18)) +>data : Symbol(data, Decl(discriminantsAndTypePredicates.ts, 64, 23)) +>Value2 : Symbol(TypeB.Value2, Decl(discriminantsAndTypePredicates.ts, 40, 18)) + } + return data; +>data : Symbol(data, Decl(discriminantsAndTypePredicates.ts, 64, 23)) +} + +function narrowToNeverUnknown(data: unknown): "Cool stuff!" | 0 { +>narrowToNeverUnknown : Symbol(narrowToNeverUnknown, Decl(discriminantsAndTypePredicates.ts, 72, 1)) +>data : Symbol(data, Decl(discriminantsAndTypePredicates.ts, 74, 30)) + + if (isType(data)) { +>isType : Symbol(isType, Decl(discriminantsAndTypePredicates.ts, 44, 26)) +>data : Symbol(data, Decl(discriminantsAndTypePredicates.ts, 74, 30)) + + if (data.Name === "TypeA") { +>data.Name : Symbol(Name, Decl(discriminantsAndTypePredicates.ts, 34, 17), Decl(discriminantsAndTypePredicates.ts, 39, 17)) +>data : Symbol(data, Decl(discriminantsAndTypePredicates.ts, 74, 30)) +>Name : Symbol(Name, Decl(discriminantsAndTypePredicates.ts, 34, 17), Decl(discriminantsAndTypePredicates.ts, 39, 17)) + + return data.Value1; +>data.Value1 : Symbol(TypeA.Value1, Decl(discriminantsAndTypePredicates.ts, 35, 18)) +>data : Symbol(data, Decl(discriminantsAndTypePredicates.ts, 74, 30)) +>Value1 : Symbol(TypeA.Value1, Decl(discriminantsAndTypePredicates.ts, 35, 18)) + } + if (data.Name === "TypeB") { +>data.Name : Symbol(TypeB.Name, Decl(discriminantsAndTypePredicates.ts, 39, 17)) +>data : Symbol(data, Decl(discriminantsAndTypePredicates.ts, 74, 30)) +>Name : Symbol(TypeB.Name, Decl(discriminantsAndTypePredicates.ts, 39, 17)) + + return data.Value2; +>data.Value2 : Symbol(TypeB.Value2, Decl(discriminantsAndTypePredicates.ts, 40, 18)) +>data : Symbol(data, Decl(discriminantsAndTypePredicates.ts, 74, 30)) +>Value2 : Symbol(TypeB.Value2, Decl(discriminantsAndTypePredicates.ts, 40, 18)) + } + return data; +>data : Symbol(data, Decl(discriminantsAndTypePredicates.ts, 74, 30)) + } + throw "error"; +} + +type Foo = { kind: "a", a: number } | { kind: "b", b: number }; +>Foo : Symbol(Foo, Decl(discriminantsAndTypePredicates.ts, 85, 1)) +>kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 87, 12)) +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 87, 23)) +>kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 87, 39)) +>b : Symbol(b, Decl(discriminantsAndTypePredicates.ts, 87, 50)) + +type Bar = { kind: "c", c: number } | { kind: "d", d: number }; +>Bar : Symbol(Bar, Decl(discriminantsAndTypePredicates.ts, 87, 63)) +>kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 88, 12)) +>c : Symbol(c, Decl(discriminantsAndTypePredicates.ts, 88, 23)) +>kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 88, 39)) +>d : Symbol(d, Decl(discriminantsAndTypePredicates.ts, 88, 50)) + +declare function isFoo(x: unknown): x is Foo; +>isFoo : Symbol(isFoo, Decl(discriminantsAndTypePredicates.ts, 88, 63)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 90, 23)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 90, 23)) +>Foo : Symbol(Foo, Decl(discriminantsAndTypePredicates.ts, 85, 1)) + +declare function isBar(x: unknown): x is Bar; +>isBar : Symbol(isBar, Decl(discriminantsAndTypePredicates.ts, 90, 45)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 91, 23)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 91, 23)) +>Bar : Symbol(Bar, Decl(discriminantsAndTypePredicates.ts, 87, 63)) + +function blah(x: unknown) { +>blah : Symbol(blah, Decl(discriminantsAndTypePredicates.ts, 91, 45)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 93, 14)) + + if (isFoo(x)) { +>isFoo : Symbol(isFoo, Decl(discriminantsAndTypePredicates.ts, 88, 63)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 93, 14)) + + if (x.kind === "a") { +>x.kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 87, 12), Decl(discriminantsAndTypePredicates.ts, 87, 39)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 93, 14)) +>kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 87, 12), Decl(discriminantsAndTypePredicates.ts, 87, 39)) + + let a = x.a; +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 96, 15)) +>x.a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 87, 23)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 93, 14)) +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 87, 23)) + } + else if (x.kind === "b") { +>x.kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 87, 39)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 93, 14)) +>kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 87, 39)) + + let b = x.b; +>b : Symbol(b, Decl(discriminantsAndTypePredicates.ts, 99, 15)) +>x.b : Symbol(b, Decl(discriminantsAndTypePredicates.ts, 87, 50)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 93, 14)) +>b : Symbol(b, Decl(discriminantsAndTypePredicates.ts, 87, 50)) + } + } + else if (isBar(x)) { +>isBar : Symbol(isBar, Decl(discriminantsAndTypePredicates.ts, 90, 45)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 93, 14)) + + if (x.kind === "c") { +>x.kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 88, 12), Decl(discriminantsAndTypePredicates.ts, 88, 39)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 93, 14)) +>kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 88, 12), Decl(discriminantsAndTypePredicates.ts, 88, 39)) + + let c = x.c; +>c : Symbol(c, Decl(discriminantsAndTypePredicates.ts, 104, 15)) +>x.c : Symbol(c, Decl(discriminantsAndTypePredicates.ts, 88, 23)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 93, 14)) +>c : Symbol(c, Decl(discriminantsAndTypePredicates.ts, 88, 23)) + } + else if (x.kind === "d") { +>x.kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 88, 39)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 93, 14)) +>kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 88, 39)) + + let d = x.d; +>d : Symbol(d, Decl(discriminantsAndTypePredicates.ts, 107, 15)) +>x.d : Symbol(d, Decl(discriminantsAndTypePredicates.ts, 88, 50)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 93, 14)) +>d : Symbol(d, Decl(discriminantsAndTypePredicates.ts, 88, 50)) + } + } + x // unknown +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 93, 14)) +} + +type PrimitiveUnion = number | string +>PrimitiveUnion : Symbol(PrimitiveUnion, Decl(discriminantsAndTypePredicates.ts, 111, 1)) + +type FooComplex = { kind: "a", a: number } | { kind: "b", b: number } | number; +>FooComplex : Symbol(FooComplex, Decl(discriminantsAndTypePredicates.ts, 113, 37)) +>kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 114, 19)) +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 114, 30)) +>kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 114, 46)) +>b : Symbol(b, Decl(discriminantsAndTypePredicates.ts, 114, 57)) + +type BarComplex = { kind: "c", c: number } | { kind: "d", d: number } | string; +>BarComplex : Symbol(BarComplex, Decl(discriminantsAndTypePredicates.ts, 114, 79)) +>kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 115, 19)) +>c : Symbol(c, Decl(discriminantsAndTypePredicates.ts, 115, 30)) +>kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 115, 46)) +>d : Symbol(d, Decl(discriminantsAndTypePredicates.ts, 115, 57)) + +declare function isPrimitiveUnion(x: unknown): x is PrimitiveUnion; +>isPrimitiveUnion : Symbol(isPrimitiveUnion, Decl(discriminantsAndTypePredicates.ts, 115, 79)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 117, 34)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 117, 34)) +>PrimitiveUnion : Symbol(PrimitiveUnion, Decl(discriminantsAndTypePredicates.ts, 111, 1)) + +declare function isFooComplex(x: unknown): x is FooComplex; +>isFooComplex : Symbol(isFooComplex, Decl(discriminantsAndTypePredicates.ts, 117, 67)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 118, 30)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 118, 30)) +>FooComplex : Symbol(FooComplex, Decl(discriminantsAndTypePredicates.ts, 113, 37)) + +declare function isBarComplex(x: unknown): x is BarComplex; +>isBarComplex : Symbol(isBarComplex, Decl(discriminantsAndTypePredicates.ts, 118, 59)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 119, 30)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 119, 30)) +>BarComplex : Symbol(BarComplex, Decl(discriminantsAndTypePredicates.ts, 114, 79)) + +declare function isZZYYComplex(x: unknown): x is { kind: "z"; zzz: string } | { kind: "y", yyy: number }; +>isZZYYComplex : Symbol(isZZYYComplex, Decl(discriminantsAndTypePredicates.ts, 119, 59)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 120, 31)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 120, 31)) +>kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 120, 50)) +>zzz : Symbol(zzz, Decl(discriminantsAndTypePredicates.ts, 120, 61)) +>kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 120, 79)) +>yyy : Symbol(yyy, Decl(discriminantsAndTypePredicates.ts, 120, 90)) + +function earlyExitsAndStuff(x: unknown) { +>earlyExitsAndStuff : Symbol(earlyExitsAndStuff, Decl(discriminantsAndTypePredicates.ts, 120, 105)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 122, 28)) + + if (!isFooComplex(x) && !isBarComplex(x)) { +>isFooComplex : Symbol(isFooComplex, Decl(discriminantsAndTypePredicates.ts, 117, 67)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 122, 28)) +>isBarComplex : Symbol(isBarComplex, Decl(discriminantsAndTypePredicates.ts, 118, 59)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 122, 28)) + + if (isZZYYComplex(x)) { +>isZZYYComplex : Symbol(isZZYYComplex, Decl(discriminantsAndTypePredicates.ts, 119, 59)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 122, 28)) + + if (x.kind !== "z") { +>x.kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 120, 50), Decl(discriminantsAndTypePredicates.ts, 120, 79)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 122, 28)) +>kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 120, 50), Decl(discriminantsAndTypePredicates.ts, 120, 79)) + + return x.yyy; +>x.yyy : Symbol(yyy, Decl(discriminantsAndTypePredicates.ts, 120, 90)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 122, 28)) +>yyy : Symbol(yyy, Decl(discriminantsAndTypePredicates.ts, 120, 90)) + } + return x.zzz; +>x.zzz : Symbol(zzz, Decl(discriminantsAndTypePredicates.ts, 120, 61)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 122, 28)) +>zzz : Symbol(zzz, Decl(discriminantsAndTypePredicates.ts, 120, 61)) + } + return; + } + if (!!isPrimitiveUnion(x)) { +>isPrimitiveUnion : Symbol(isPrimitiveUnion, Decl(discriminantsAndTypePredicates.ts, 115, 79)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 122, 28)) + + return x; +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 122, 28)) + } + if (!isZZYYComplex(x)) { +>isZZYYComplex : Symbol(isZZYYComplex, Decl(discriminantsAndTypePredicates.ts, 119, 59)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 122, 28)) + + if (x.kind === "a") { +>x.kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 114, 19), Decl(discriminantsAndTypePredicates.ts, 114, 46), Decl(discriminantsAndTypePredicates.ts, 115, 19), Decl(discriminantsAndTypePredicates.ts, 115, 46)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 122, 28)) +>kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 114, 19), Decl(discriminantsAndTypePredicates.ts, 114, 46), Decl(discriminantsAndTypePredicates.ts, 115, 19), Decl(discriminantsAndTypePredicates.ts, 115, 46)) + + let a = x.a; +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 137, 15)) +>x.a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 114, 30)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 122, 28)) +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 114, 30)) + } + if (x.kind === "b") { +>x.kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 114, 19), Decl(discriminantsAndTypePredicates.ts, 114, 46), Decl(discriminantsAndTypePredicates.ts, 115, 19), Decl(discriminantsAndTypePredicates.ts, 115, 46)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 122, 28)) +>kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 114, 19), Decl(discriminantsAndTypePredicates.ts, 114, 46), Decl(discriminantsAndTypePredicates.ts, 115, 19), Decl(discriminantsAndTypePredicates.ts, 115, 46)) + + let b = x.b; +>b : Symbol(b, Decl(discriminantsAndTypePredicates.ts, 140, 15)) +>x.b : Symbol(b, Decl(discriminantsAndTypePredicates.ts, 114, 57)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 122, 28)) +>b : Symbol(b, Decl(discriminantsAndTypePredicates.ts, 114, 57)) + } + if (x.kind === "c") { +>x.kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 114, 19), Decl(discriminantsAndTypePredicates.ts, 114, 46), Decl(discriminantsAndTypePredicates.ts, 115, 19), Decl(discriminantsAndTypePredicates.ts, 115, 46)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 122, 28)) +>kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 114, 19), Decl(discriminantsAndTypePredicates.ts, 114, 46), Decl(discriminantsAndTypePredicates.ts, 115, 19), Decl(discriminantsAndTypePredicates.ts, 115, 46)) + + let c = x.c; +>c : Symbol(c, Decl(discriminantsAndTypePredicates.ts, 143, 15)) +>x.c : Symbol(c, Decl(discriminantsAndTypePredicates.ts, 115, 30)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 122, 28)) +>c : Symbol(c, Decl(discriminantsAndTypePredicates.ts, 115, 30)) + } + if (x.kind === "d") { +>x.kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 114, 19), Decl(discriminantsAndTypePredicates.ts, 114, 46), Decl(discriminantsAndTypePredicates.ts, 115, 19), Decl(discriminantsAndTypePredicates.ts, 115, 46)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 122, 28)) +>kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 114, 19), Decl(discriminantsAndTypePredicates.ts, 114, 46), Decl(discriminantsAndTypePredicates.ts, 115, 19), Decl(discriminantsAndTypePredicates.ts, 115, 46)) + + let d = x.d; +>d : Symbol(d, Decl(discriminantsAndTypePredicates.ts, 146, 15)) +>x.d : Symbol(d, Decl(discriminantsAndTypePredicates.ts, 115, 57)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 122, 28)) +>d : Symbol(d, Decl(discriminantsAndTypePredicates.ts, 115, 57)) + } + } +} + +function bluergh(x: unknown) { +>bluergh : Symbol(bluergh, Decl(discriminantsAndTypePredicates.ts, 149, 1)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 151, 17)) + + if (isPrimitiveUnion(x)) { +>isPrimitiveUnion : Symbol(isPrimitiveUnion, Decl(discriminantsAndTypePredicates.ts, 115, 79)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 151, 17)) + + let a: number | string = x; +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 153, 11)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 151, 17)) + + return; + } + if (isFooComplex(x) && typeof x === "object") { +>isFooComplex : Symbol(isFooComplex, Decl(discriminantsAndTypePredicates.ts, 117, 67)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 151, 17)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 151, 17)) + + if (x.kind === "a") { +>x.kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 114, 19), Decl(discriminantsAndTypePredicates.ts, 114, 46)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 151, 17)) +>kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 114, 19), Decl(discriminantsAndTypePredicates.ts, 114, 46)) + + let a = x.a; +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 158, 15)) +>x.a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 114, 30)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 151, 17)) +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 114, 30)) + } + else if (x.kind === "b") { +>x.kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 114, 46)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 151, 17)) +>kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 114, 46)) + + let b = x.b; +>b : Symbol(b, Decl(discriminantsAndTypePredicates.ts, 161, 15)) +>x.b : Symbol(b, Decl(discriminantsAndTypePredicates.ts, 114, 57)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 151, 17)) +>b : Symbol(b, Decl(discriminantsAndTypePredicates.ts, 114, 57)) + } + } + if (isPrimitiveUnion(x)) { +>isPrimitiveUnion : Symbol(isPrimitiveUnion, Decl(discriminantsAndTypePredicates.ts, 115, 79)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 151, 17)) + + let a: number | string = x; +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 165, 11)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 151, 17)) + } + if (isBarComplex(x) && typeof x === "object") { +>isBarComplex : Symbol(isBarComplex, Decl(discriminantsAndTypePredicates.ts, 118, 59)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 151, 17)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 151, 17)) + + if (x.kind === "c") { +>x.kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 115, 19), Decl(discriminantsAndTypePredicates.ts, 115, 46)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 151, 17)) +>kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 115, 19), Decl(discriminantsAndTypePredicates.ts, 115, 46)) + + let c = x.c; +>c : Symbol(c, Decl(discriminantsAndTypePredicates.ts, 169, 15)) +>x.c : Symbol(c, Decl(discriminantsAndTypePredicates.ts, 115, 30)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 151, 17)) +>c : Symbol(c, Decl(discriminantsAndTypePredicates.ts, 115, 30)) + } + else if (x.kind === "d") { +>x.kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 115, 46)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 151, 17)) +>kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 115, 46)) + + let d = x.d; +>d : Symbol(d, Decl(discriminantsAndTypePredicates.ts, 172, 15)) +>x.d : Symbol(d, Decl(discriminantsAndTypePredicates.ts, 115, 57)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 151, 17)) +>d : Symbol(d, Decl(discriminantsAndTypePredicates.ts, 115, 57)) + } + } + if (isPrimitiveUnion(x)) { +>isPrimitiveUnion : Symbol(isPrimitiveUnion, Decl(discriminantsAndTypePredicates.ts, 115, 79)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 151, 17)) + + let a: number | string = x; +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 176, 11)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 151, 17)) + } + x // unknown +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 151, 17)) +} + +type A1 = { x: number }; +>A1 : Symbol(A1, Decl(discriminantsAndTypePredicates.ts, 179, 1)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 181, 11)) + +type B1 = A1 & { kind: "B"; y: number }; +>B1 : Symbol(B1, Decl(discriminantsAndTypePredicates.ts, 181, 24)) +>A1 : Symbol(A1, Decl(discriminantsAndTypePredicates.ts, 179, 1)) +>kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 182, 16)) +>y : Symbol(y, Decl(discriminantsAndTypePredicates.ts, 182, 27)) + +type C1 = A1 & { kind: "C"; z: number }; +>C1 : Symbol(C1, Decl(discriminantsAndTypePredicates.ts, 182, 40)) +>A1 : Symbol(A1, Decl(discriminantsAndTypePredicates.ts, 179, 1)) +>kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 183, 16)) +>z : Symbol(z, Decl(discriminantsAndTypePredicates.ts, 183, 27)) + +function isBorC(a: A1): a is B1 | C1 { +>isBorC : Symbol(isBorC, Decl(discriminantsAndTypePredicates.ts, 183, 40)) +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 185, 16)) +>A1 : Symbol(A1, Decl(discriminantsAndTypePredicates.ts, 179, 1)) +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 185, 16)) +>B1 : Symbol(B1, Decl(discriminantsAndTypePredicates.ts, 181, 24)) +>C1 : Symbol(C1, Decl(discriminantsAndTypePredicates.ts, 182, 40)) + + return (a as any).kind === "B" || (a as any).kind === "C"; +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 185, 16)) +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 185, 16)) +} + +function isB1(a: A1): a is B1 { +>isB1 : Symbol(isB1, Decl(discriminantsAndTypePredicates.ts, 187, 1)) +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 189, 14)) +>A1 : Symbol(A1, Decl(discriminantsAndTypePredicates.ts, 179, 1)) +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 189, 14)) +>B1 : Symbol(B1, Decl(discriminantsAndTypePredicates.ts, 181, 24)) + + return (a as any).kind === "B"; +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 189, 14)) +} + +function isC1(a: A1): a is C1 { +>isC1 : Symbol(isC1, Decl(discriminantsAndTypePredicates.ts, 191, 1)) +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 193, 14)) +>A1 : Symbol(A1, Decl(discriminantsAndTypePredicates.ts, 179, 1)) +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 193, 14)) +>C1 : Symbol(C1, Decl(discriminantsAndTypePredicates.ts, 182, 40)) + + return (a as any).kind === "C"; +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 193, 14)) +} + +function fn1(a: A1) { +>fn1 : Symbol(fn1, Decl(discriminantsAndTypePredicates.ts, 195, 1)) +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 197, 13)) +>A1 : Symbol(A1, Decl(discriminantsAndTypePredicates.ts, 179, 1)) + + if (isBorC(a)) { +>isBorC : Symbol(isBorC, Decl(discriminantsAndTypePredicates.ts, 183, 40)) +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 197, 13)) + + if (a.kind === "B") { +>a.kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 182, 16), Decl(discriminantsAndTypePredicates.ts, 183, 16)) +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 197, 13)) +>kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 182, 16), Decl(discriminantsAndTypePredicates.ts, 183, 16)) + + a.y; +>a.y : Symbol(y, Decl(discriminantsAndTypePredicates.ts, 182, 27)) +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 197, 13)) +>y : Symbol(y, Decl(discriminantsAndTypePredicates.ts, 182, 27)) + } + } +} + +function fn2(a: A1) { +>fn2 : Symbol(fn2, Decl(discriminantsAndTypePredicates.ts, 203, 1)) +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 205, 13)) +>A1 : Symbol(A1, Decl(discriminantsAndTypePredicates.ts, 179, 1)) + + if (!isB1(a)) { +>isB1 : Symbol(isB1, Decl(discriminantsAndTypePredicates.ts, 187, 1)) +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 205, 13)) + + return; + } + if (!isC1(a)) { +>isC1 : Symbol(isC1, Decl(discriminantsAndTypePredicates.ts, 191, 1)) +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 205, 13)) + + if (a.kind === "B") { +>a.kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 182, 16)) +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 205, 13)) +>kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 182, 16)) + + a.y; +>a.y : Symbol(y, Decl(discriminantsAndTypePredicates.ts, 182, 27)) +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 205, 13)) +>y : Symbol(y, Decl(discriminantsAndTypePredicates.ts, 182, 27)) + } + return; + } + if (a.kind === "B") { +>a.kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 182, 16), Decl(discriminantsAndTypePredicates.ts, 183, 16)) +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 205, 13)) +>kind : Symbol(kind, Decl(discriminantsAndTypePredicates.ts, 182, 16), Decl(discriminantsAndTypePredicates.ts, 183, 16)) + + a.y; +>a.y : Symbol(y, Decl(discriminantsAndTypePredicates.ts, 182, 27)) +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 205, 13)) +>y : Symbol(y, Decl(discriminantsAndTypePredicates.ts, 182, 27)) + } +} + +declare function isTypeObj(x: unknown): x is { kind1: string, a?: number, b?: number }; +>isTypeObj : Symbol(isTypeObj, Decl(discriminantsAndTypePredicates.ts, 218, 1)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 220, 27)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 220, 27)) +>kind1 : Symbol(kind1, Decl(discriminantsAndTypePredicates.ts, 220, 46)) +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 220, 61)) +>b : Symbol(b, Decl(discriminantsAndTypePredicates.ts, 220, 73)) + +declare function isTypeAB(x: unknown): x is { kind1: 'a', a: 1 } | { kind1: 'b', b: 2 }; +>isTypeAB : Symbol(isTypeAB, Decl(discriminantsAndTypePredicates.ts, 220, 87)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 221, 26)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 221, 26)) +>kind1 : Symbol(kind1, Decl(discriminantsAndTypePredicates.ts, 221, 45)) +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 221, 57)) +>kind1 : Symbol(kind1, Decl(discriminantsAndTypePredicates.ts, 221, 68)) +>b : Symbol(b, Decl(discriminantsAndTypePredicates.ts, 221, 80)) + +declare function isTypeCD(x: unknown): x is { kind2: 'c', c: 3 } | { kind2: 'd', d: 4 }; +>isTypeCD : Symbol(isTypeCD, Decl(discriminantsAndTypePredicates.ts, 221, 88)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 222, 26)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 222, 26)) +>kind2 : Symbol(kind2, Decl(discriminantsAndTypePredicates.ts, 222, 45)) +>c : Symbol(c, Decl(discriminantsAndTypePredicates.ts, 222, 57)) +>kind2 : Symbol(kind2, Decl(discriminantsAndTypePredicates.ts, 222, 68)) +>d : Symbol(d, Decl(discriminantsAndTypePredicates.ts, 222, 80)) + +function testComposition1(x: unknown) { +>testComposition1 : Symbol(testComposition1, Decl(discriminantsAndTypePredicates.ts, 222, 88)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 224, 26)) + + if (isTypeAB(x)) { +>isTypeAB : Symbol(isTypeAB, Decl(discriminantsAndTypePredicates.ts, 220, 87)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 224, 26)) + + if (isTypeCD(x)) { +>isTypeCD : Symbol(isTypeCD, Decl(discriminantsAndTypePredicates.ts, 221, 88)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 224, 26)) + + if (x.kind1 === 'a') { +>x.kind1 : Symbol(kind1, Decl(discriminantsAndTypePredicates.ts, 221, 45), Decl(discriminantsAndTypePredicates.ts, 221, 68)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 224, 26)) +>kind1 : Symbol(kind1, Decl(discriminantsAndTypePredicates.ts, 221, 45), Decl(discriminantsAndTypePredicates.ts, 221, 68)) + + x.a; +>x.a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 221, 57)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 224, 26)) +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 221, 57)) + } + if (x.kind2 === 'c') { +>x.kind2 : Symbol(kind2, Decl(discriminantsAndTypePredicates.ts, 222, 45), Decl(discriminantsAndTypePredicates.ts, 222, 68)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 224, 26)) +>kind2 : Symbol(kind2, Decl(discriminantsAndTypePredicates.ts, 222, 45), Decl(discriminantsAndTypePredicates.ts, 222, 68)) + + x.c; +>x.c : Symbol(c, Decl(discriminantsAndTypePredicates.ts, 222, 57)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 224, 26)) +>c : Symbol(c, Decl(discriminantsAndTypePredicates.ts, 222, 57)) + } + } + if (x.kind1 === 'a') { +>x.kind1 : Symbol(kind1, Decl(discriminantsAndTypePredicates.ts, 221, 45), Decl(discriminantsAndTypePredicates.ts, 221, 68)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 224, 26)) +>kind1 : Symbol(kind1, Decl(discriminantsAndTypePredicates.ts, 221, 45), Decl(discriminantsAndTypePredicates.ts, 221, 68)) + + x.a; +>x.a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 221, 57)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 224, 26)) +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 221, 57)) + } + if (x.kind2 === 'c') { +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 224, 26)) + + x.c; +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 224, 26)) + } + } +} + +function testComposition2(x: unknown) { +>testComposition2 : Symbol(testComposition2, Decl(discriminantsAndTypePredicates.ts, 241, 1)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 243, 26)) + + if (isTypeObj(x)) { +>isTypeObj : Symbol(isTypeObj, Decl(discriminantsAndTypePredicates.ts, 218, 1)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 243, 26)) + + if (isTypeAB(x)) { +>isTypeAB : Symbol(isTypeAB, Decl(discriminantsAndTypePredicates.ts, 220, 87)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 243, 26)) + + if (x.kind1 === "a") { +>x.kind1 : Symbol(kind1, Decl(discriminantsAndTypePredicates.ts, 221, 45), Decl(discriminantsAndTypePredicates.ts, 221, 68)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 243, 26)) +>kind1 : Symbol(kind1, Decl(discriminantsAndTypePredicates.ts, 221, 45), Decl(discriminantsAndTypePredicates.ts, 221, 68)) + + x.a; +>x.a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 221, 57)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 243, 26)) +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 221, 57)) + } + } + if (x.kind1 === "a") { +>x.kind1 : Symbol(kind1, Decl(discriminantsAndTypePredicates.ts, 220, 46), Decl(discriminantsAndTypePredicates.ts, 221, 45), Decl(discriminantsAndTypePredicates.ts, 221, 68)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 243, 26)) +>kind1 : Symbol(kind1, Decl(discriminantsAndTypePredicates.ts, 220, 46), Decl(discriminantsAndTypePredicates.ts, 221, 45), Decl(discriminantsAndTypePredicates.ts, 221, 68)) + + x.a; // Error +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 243, 26)) + } + } +} + +function testComposition3(x: unknown) { +>testComposition3 : Symbol(testComposition3, Decl(discriminantsAndTypePredicates.ts, 254, 1)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 256, 26)) + + if (isTypeAB(x)) { +>isTypeAB : Symbol(isTypeAB, Decl(discriminantsAndTypePredicates.ts, 220, 87)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 256, 26)) + + if (x.kind1 === 'a') { +>x.kind1 : Symbol(kind1, Decl(discriminantsAndTypePredicates.ts, 221, 45), Decl(discriminantsAndTypePredicates.ts, 221, 68)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 256, 26)) +>kind1 : Symbol(kind1, Decl(discriminantsAndTypePredicates.ts, 221, 45), Decl(discriminantsAndTypePredicates.ts, 221, 68)) + + x.a; +>x.a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 221, 57)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 256, 26)) +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 221, 57)) + } + return; + } + if (x.kind1 === 'a') { +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 256, 26)) + + x.a; // Error +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 256, 26)) + } + if (isTypeCD(x)) { +>isTypeCD : Symbol(isTypeCD, Decl(discriminantsAndTypePredicates.ts, 221, 88)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 256, 26)) + + if (x.kind2 === 'c') { +>x.kind2 : Symbol(kind2, Decl(discriminantsAndTypePredicates.ts, 222, 45), Decl(discriminantsAndTypePredicates.ts, 222, 68)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 256, 26)) +>kind2 : Symbol(kind2, Decl(discriminantsAndTypePredicates.ts, 222, 45), Decl(discriminantsAndTypePredicates.ts, 222, 68)) + + x.c; +>x.c : Symbol(c, Decl(discriminantsAndTypePredicates.ts, 222, 57)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 256, 26)) +>c : Symbol(c, Decl(discriminantsAndTypePredicates.ts, 222, 57)) + } + return; + } + if (x.kind2 === 'c') { +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 256, 26)) + + x.c; // Error +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 256, 26)) + } + if (isTypeAB(x)) { +>isTypeAB : Symbol(isTypeAB, Decl(discriminantsAndTypePredicates.ts, 220, 87)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 256, 26)) + + if (isTypeCD(x)) { +>isTypeCD : Symbol(isTypeCD, Decl(discriminantsAndTypePredicates.ts, 221, 88)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 256, 26)) + + if (x.kind1 === 'a') { +>x.kind1 : Symbol(kind1, Decl(discriminantsAndTypePredicates.ts, 221, 45), Decl(discriminantsAndTypePredicates.ts, 221, 68)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 256, 26)) +>kind1 : Symbol(kind1, Decl(discriminantsAndTypePredicates.ts, 221, 45), Decl(discriminantsAndTypePredicates.ts, 221, 68)) + + x.a; +>x.a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 221, 57)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 256, 26)) +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 221, 57)) + } + if (x.kind2 === 'c') { +>x.kind2 : Symbol(kind2, Decl(discriminantsAndTypePredicates.ts, 222, 45), Decl(discriminantsAndTypePredicates.ts, 222, 68)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 256, 26)) +>kind2 : Symbol(kind2, Decl(discriminantsAndTypePredicates.ts, 222, 45), Decl(discriminantsAndTypePredicates.ts, 222, 68)) + + x.c; +>x.c : Symbol(c, Decl(discriminantsAndTypePredicates.ts, 222, 57)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 256, 26)) +>c : Symbol(c, Decl(discriminantsAndTypePredicates.ts, 222, 57)) + } + } + } +} + +function looper(getter: () => unknown) { +>looper : Symbol(looper, Decl(discriminantsAndTypePredicates.ts, 285, 1)) +>getter : Symbol(getter, Decl(discriminantsAndTypePredicates.ts, 287, 16)) + + let x = getter(); +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 288, 7)) +>getter : Symbol(getter, Decl(discriminantsAndTypePredicates.ts, 287, 16)) + + while (isTypeAB(x)) { +>isTypeAB : Symbol(isTypeAB, Decl(discriminantsAndTypePredicates.ts, 220, 87)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 288, 7)) + + if (isTypeCD(x)) { +>isTypeCD : Symbol(isTypeCD, Decl(discriminantsAndTypePredicates.ts, 221, 88)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 288, 7)) + + if (x.kind1 === 'a') { +>x.kind1 : Symbol(kind1, Decl(discriminantsAndTypePredicates.ts, 221, 45), Decl(discriminantsAndTypePredicates.ts, 221, 68)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 288, 7)) +>kind1 : Symbol(kind1, Decl(discriminantsAndTypePredicates.ts, 221, 45), Decl(discriminantsAndTypePredicates.ts, 221, 68)) + + x.a; +>x.a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 221, 57)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 288, 7)) +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 221, 57)) + } + if (x.kind2 === 'c') { +>x.kind2 : Symbol(kind2, Decl(discriminantsAndTypePredicates.ts, 222, 45), Decl(discriminantsAndTypePredicates.ts, 222, 68)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 288, 7)) +>kind2 : Symbol(kind2, Decl(discriminantsAndTypePredicates.ts, 222, 45), Decl(discriminantsAndTypePredicates.ts, 222, 68)) + + x.c; +>x.c : Symbol(c, Decl(discriminantsAndTypePredicates.ts, 222, 57)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 288, 7)) +>c : Symbol(c, Decl(discriminantsAndTypePredicates.ts, 222, 57)) + } + } + if (x.kind1 === 'a') { +>x.kind1 : Symbol(kind1, Decl(discriminantsAndTypePredicates.ts, 221, 45), Decl(discriminantsAndTypePredicates.ts, 221, 68)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 288, 7)) +>kind1 : Symbol(kind1, Decl(discriminantsAndTypePredicates.ts, 221, 45), Decl(discriminantsAndTypePredicates.ts, 221, 68)) + + x.a; +>x.a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 221, 57)) +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 288, 7)) +>a : Symbol(a, Decl(discriminantsAndTypePredicates.ts, 221, 57)) + } + if (x.kind2 === 'c') { +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 288, 7)) + + x.c; // Error +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 288, 7)) + } + } + if (x.kind1 === 'a') { +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 288, 7)) + + x.a; // error +>x : Symbol(x, Decl(discriminantsAndTypePredicates.ts, 288, 7)) + } +} + +interface Success { +>Success : Symbol(Success, Decl(discriminantsAndTypePredicates.ts, 308, 1)) + + success: true; +>success : Symbol(Success.success, Decl(discriminantsAndTypePredicates.ts, 310, 19)) + + response: object; +>response : Symbol(Success.response, Decl(discriminantsAndTypePredicates.ts, 311, 18)) +} + +interface Error { +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(discriminantsAndTypePredicates.ts, 313, 1)) + + success: false; +>success : Symbol(Error.success, Decl(discriminantsAndTypePredicates.ts, 315, 17)) + + error: object; +>error : Symbol(Error.error, Decl(discriminantsAndTypePredicates.ts, 316, 19)) +} + +function request(): Success | Error { +>request : Symbol(request, Decl(discriminantsAndTypePredicates.ts, 318, 1)) +>Success : Symbol(Success, Decl(discriminantsAndTypePredicates.ts, 308, 1)) +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(discriminantsAndTypePredicates.ts, 313, 1)) + + return null as any; +} + +// This does not work: +let r +>r : Symbol(r, Decl(discriminantsAndTypePredicates.ts, 325, 3)) + +r = request(); +>r : Symbol(r, Decl(discriminantsAndTypePredicates.ts, 325, 3)) +>request : Symbol(request, Decl(discriminantsAndTypePredicates.ts, 318, 1)) + +if (r.success) { +>r : Symbol(r, Decl(discriminantsAndTypePredicates.ts, 325, 3)) + + r.response; +>r : Symbol(r, Decl(discriminantsAndTypePredicates.ts, 325, 3)) +} + diff --git a/tests/baselines/reference/discriminantsAndTypePredicates.types b/tests/baselines/reference/discriminantsAndTypePredicates.types index 73e05ac59c421..72e4e500aeec1 100644 --- a/tests/baselines/reference/discriminantsAndTypePredicates.types +++ b/tests/baselines/reference/discriminantsAndTypePredicates.types @@ -88,3 +88,998 @@ function foo2(x: A | B): any { x; // never >x : never } + +// Repro from #30557 + +interface TypeA { + Name: "TypeA"; +>Name : "TypeA" + + Value1: "Cool stuff!"; +>Value1 : "Cool stuff!" +} + +interface TypeB { + Name: "TypeB"; +>Name : "TypeB" + + Value2: 0; +>Value2 : 0 +} + +type Type = TypeA | TypeB; +>Type : Type + +declare function isType(x: unknown): x is Type; +>isType : (x: unknown) => x is Type +>x : unknown + +function WorksProperly(data: Type) { +>WorksProperly : (data: Type) => void +>data : Type + + if (data.Name === "TypeA") { +>data.Name === "TypeA" : boolean +>data.Name : "TypeA" | "TypeB" +>data : Type +>Name : "TypeA" | "TypeB" +>"TypeA" : "TypeA" + + // TypeA + const value1 = data.Value1; +>value1 : "Cool stuff!" +>data.Value1 : "Cool stuff!" +>data : TypeA +>Value1 : "Cool stuff!" + } +} + +function DoesNotWork(data: unknown) { +>DoesNotWork : (data: unknown) => void +>data : unknown + + if (isType(data)) { +>isType(data) : boolean +>isType : (x: unknown) => x is Type +>data : unknown + + if (data.Name === "TypeA") { +>data.Name === "TypeA" : boolean +>data.Name : "TypeA" | "TypeB" +>data : Type +>Name : "TypeA" | "TypeB" +>"TypeA" : "TypeA" + + // TypeA + const value1 = data.Value1; +>value1 : "Cool stuff!" +>data.Value1 : "Cool stuff!" +>data : TypeA +>Value1 : "Cool stuff!" + } + } +} + +function narrowToNever(data: Type): "Cool stuff!" | 0 { +>narrowToNever : (data: Type) => 0 | "Cool stuff!" +>data : Type + + if (data.Name === "TypeA") { +>data.Name === "TypeA" : boolean +>data.Name : "TypeA" | "TypeB" +>data : Type +>Name : "TypeA" | "TypeB" +>"TypeA" : "TypeA" + + return data.Value1; +>data.Value1 : "Cool stuff!" +>data : TypeA +>Value1 : "Cool stuff!" + } + if (data.Name === "TypeB") { +>data.Name === "TypeB" : boolean +>data.Name : "TypeB" +>data : TypeB +>Name : "TypeB" +>"TypeB" : "TypeB" + + return data.Value2; +>data.Value2 : 0 +>data : TypeB +>Value2 : 0 + } + return data; +>data : never +} + +function narrowToNeverUnknown(data: unknown): "Cool stuff!" | 0 { +>narrowToNeverUnknown : (data: unknown) => 0 | "Cool stuff!" +>data : unknown + + if (isType(data)) { +>isType(data) : boolean +>isType : (x: unknown) => x is Type +>data : unknown + + if (data.Name === "TypeA") { +>data.Name === "TypeA" : boolean +>data.Name : "TypeA" | "TypeB" +>data : Type +>Name : "TypeA" | "TypeB" +>"TypeA" : "TypeA" + + return data.Value1; +>data.Value1 : "Cool stuff!" +>data : TypeA +>Value1 : "Cool stuff!" + } + if (data.Name === "TypeB") { +>data.Name === "TypeB" : boolean +>data.Name : "TypeB" +>data : TypeB +>Name : "TypeB" +>"TypeB" : "TypeB" + + return data.Value2; +>data.Value2 : 0 +>data : TypeB +>Value2 : 0 + } + return data; +>data : never + } + throw "error"; +>"error" : "error" +} + +type Foo = { kind: "a", a: number } | { kind: "b", b: number }; +>Foo : Foo +>kind : "a" +>a : number +>kind : "b" +>b : number + +type Bar = { kind: "c", c: number } | { kind: "d", d: number }; +>Bar : Bar +>kind : "c" +>c : number +>kind : "d" +>d : number + +declare function isFoo(x: unknown): x is Foo; +>isFoo : (x: unknown) => x is Foo +>x : unknown + +declare function isBar(x: unknown): x is Bar; +>isBar : (x: unknown) => x is Bar +>x : unknown + +function blah(x: unknown) { +>blah : (x: unknown) => void +>x : unknown + + if (isFoo(x)) { +>isFoo(x) : boolean +>isFoo : (x: unknown) => x is Foo +>x : unknown + + if (x.kind === "a") { +>x.kind === "a" : boolean +>x.kind : "a" | "b" +>x : Foo +>kind : "a" | "b" +>"a" : "a" + + let a = x.a; +>a : number +>x.a : number +>x : { kind: "a"; a: number; } +>a : number + } + else if (x.kind === "b") { +>x.kind === "b" : boolean +>x.kind : "b" +>x : { kind: "b"; b: number; } +>kind : "b" +>"b" : "b" + + let b = x.b; +>b : number +>x.b : number +>x : { kind: "b"; b: number; } +>b : number + } + } + else if (isBar(x)) { +>isBar(x) : boolean +>isBar : (x: unknown) => x is Bar +>x : unknown + + if (x.kind === "c") { +>x.kind === "c" : boolean +>x.kind : "c" | "d" +>x : Bar +>kind : "c" | "d" +>"c" : "c" + + let c = x.c; +>c : number +>x.c : number +>x : { kind: "c"; c: number; } +>c : number + } + else if (x.kind === "d") { +>x.kind === "d" : boolean +>x.kind : "d" +>x : { kind: "d"; d: number; } +>kind : "d" +>"d" : "d" + + let d = x.d; +>d : number +>x.d : number +>x : { kind: "d"; d: number; } +>d : number + } + } + x // unknown +>x : unknown +} + +type PrimitiveUnion = number | string +>PrimitiveUnion : PrimitiveUnion + +type FooComplex = { kind: "a", a: number } | { kind: "b", b: number } | number; +>FooComplex : FooComplex +>kind : "a" +>a : number +>kind : "b" +>b : number + +type BarComplex = { kind: "c", c: number } | { kind: "d", d: number } | string; +>BarComplex : BarComplex +>kind : "c" +>c : number +>kind : "d" +>d : number + +declare function isPrimitiveUnion(x: unknown): x is PrimitiveUnion; +>isPrimitiveUnion : (x: unknown) => x is PrimitiveUnion +>x : unknown + +declare function isFooComplex(x: unknown): x is FooComplex; +>isFooComplex : (x: unknown) => x is FooComplex +>x : unknown + +declare function isBarComplex(x: unknown): x is BarComplex; +>isBarComplex : (x: unknown) => x is BarComplex +>x : unknown + +declare function isZZYYComplex(x: unknown): x is { kind: "z"; zzz: string } | { kind: "y", yyy: number }; +>isZZYYComplex : (x: unknown) => x is { kind: "z"; zzz: string; } | { kind: "y"; yyy: number; } +>x : unknown +>kind : "z" +>zzz : string +>kind : "y" +>yyy : number + +function earlyExitsAndStuff(x: unknown) { +>earlyExitsAndStuff : (x: unknown) => PrimitiveUnion +>x : unknown + + if (!isFooComplex(x) && !isBarComplex(x)) { +>!isFooComplex(x) && !isBarComplex(x) : boolean +>!isFooComplex(x) : boolean +>isFooComplex(x) : boolean +>isFooComplex : (x: unknown) => x is FooComplex +>x : unknown +>!isBarComplex(x) : boolean +>isBarComplex(x) : boolean +>isBarComplex : (x: unknown) => x is BarComplex +>x : unknown + + if (isZZYYComplex(x)) { +>isZZYYComplex(x) : boolean +>isZZYYComplex : (x: unknown) => x is { kind: "z"; zzz: string; } | { kind: "y"; yyy: number; } +>x : unknown + + if (x.kind !== "z") { +>x.kind !== "z" : boolean +>x.kind : "z" | "y" +>x : { kind: "z"; zzz: string; } | { kind: "y"; yyy: number; } +>kind : "z" | "y" +>"z" : "z" + + return x.yyy; +>x.yyy : number +>x : { kind: "y"; yyy: number; } +>yyy : number + } + return x.zzz; +>x.zzz : string +>x : { kind: "z"; zzz: string; } +>zzz : string + } + return; + } + if (!!isPrimitiveUnion(x)) { +>!!isPrimitiveUnion(x) : boolean +>!isPrimitiveUnion(x) : boolean +>isPrimitiveUnion(x) : boolean +>isPrimitiveUnion : (x: unknown) => x is PrimitiveUnion +>x : string | number | { kind: "a"; a: number; } | { kind: "b"; b: number; } | { kind: "c"; c: number; } | { kind: "d"; d: number; } + + return x; +>x : PrimitiveUnion + } + if (!isZZYYComplex(x)) { +>!isZZYYComplex(x) : boolean +>isZZYYComplex(x) : boolean +>isZZYYComplex : (x: unknown) => x is { kind: "z"; zzz: string; } | { kind: "y"; yyy: number; } +>x : { kind: "a"; a: number; } | { kind: "b"; b: number; } | { kind: "c"; c: number; } | { kind: "d"; d: number; } + + if (x.kind === "a") { +>x.kind === "a" : boolean +>x.kind : "a" | "b" | "c" | "d" +>x : { kind: "a"; a: number; } | { kind: "b"; b: number; } | { kind: "c"; c: number; } | { kind: "d"; d: number; } +>kind : "a" | "b" | "c" | "d" +>"a" : "a" + + let a = x.a; +>a : number +>x.a : number +>x : { kind: "a"; a: number; } +>a : number + } + if (x.kind === "b") { +>x.kind === "b" : boolean +>x.kind : "a" | "b" | "c" | "d" +>x : { kind: "a"; a: number; } | { kind: "b"; b: number; } | { kind: "c"; c: number; } | { kind: "d"; d: number; } +>kind : "a" | "b" | "c" | "d" +>"b" : "b" + + let b = x.b; +>b : number +>x.b : number +>x : { kind: "b"; b: number; } +>b : number + } + if (x.kind === "c") { +>x.kind === "c" : boolean +>x.kind : "a" | "b" | "c" | "d" +>x : { kind: "a"; a: number; } | { kind: "b"; b: number; } | { kind: "c"; c: number; } | { kind: "d"; d: number; } +>kind : "a" | "b" | "c" | "d" +>"c" : "c" + + let c = x.c; +>c : number +>x.c : number +>x : { kind: "c"; c: number; } +>c : number + } + if (x.kind === "d") { +>x.kind === "d" : boolean +>x.kind : "a" | "b" | "c" | "d" +>x : { kind: "a"; a: number; } | { kind: "b"; b: number; } | { kind: "c"; c: number; } | { kind: "d"; d: number; } +>kind : "a" | "b" | "c" | "d" +>"d" : "d" + + let d = x.d; +>d : number +>x.d : number +>x : { kind: "d"; d: number; } +>d : number + } + } +} + +function bluergh(x: unknown) { +>bluergh : (x: unknown) => void +>x : unknown + + if (isPrimitiveUnion(x)) { +>isPrimitiveUnion(x) : boolean +>isPrimitiveUnion : (x: unknown) => x is PrimitiveUnion +>x : unknown + + let a: number | string = x; +>a : PrimitiveUnion +>x : PrimitiveUnion + + return; + } + if (isFooComplex(x) && typeof x === "object") { +>isFooComplex(x) && typeof x === "object" : boolean +>isFooComplex(x) : boolean +>isFooComplex : (x: unknown) => x is FooComplex +>x : unknown +>typeof x === "object" : boolean +>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>x : FooComplex +>"object" : "object" + + if (x.kind === "a") { +>x.kind === "a" : boolean +>x.kind : "a" | "b" +>x : { kind: "a"; a: number; } | { kind: "b"; b: number; } +>kind : "a" | "b" +>"a" : "a" + + let a = x.a; +>a : number +>x.a : number +>x : { kind: "a"; a: number; } +>a : number + } + else if (x.kind === "b") { +>x.kind === "b" : boolean +>x.kind : "b" +>x : { kind: "b"; b: number; } +>kind : "b" +>"b" : "b" + + let b = x.b; +>b : number +>x.b : number +>x : { kind: "b"; b: number; } +>b : number + } + } + if (isPrimitiveUnion(x)) { +>isPrimitiveUnion(x) : boolean +>isPrimitiveUnion : (x: unknown) => x is PrimitiveUnion +>x : unknown + + let a: number | string = x; +>a : PrimitiveUnion +>x : PrimitiveUnion + } + if (isBarComplex(x) && typeof x === "object") { +>isBarComplex(x) && typeof x === "object" : boolean +>isBarComplex(x) : boolean +>isBarComplex : (x: unknown) => x is BarComplex +>x : unknown +>typeof x === "object" : boolean +>typeof x : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>x : BarComplex +>"object" : "object" + + if (x.kind === "c") { +>x.kind === "c" : boolean +>x.kind : "c" | "d" +>x : { kind: "c"; c: number; } | { kind: "d"; d: number; } +>kind : "c" | "d" +>"c" : "c" + + let c = x.c; +>c : number +>x.c : number +>x : { kind: "c"; c: number; } +>c : number + } + else if (x.kind === "d") { +>x.kind === "d" : boolean +>x.kind : "d" +>x : { kind: "d"; d: number; } +>kind : "d" +>"d" : "d" + + let d = x.d; +>d : number +>x.d : number +>x : { kind: "d"; d: number; } +>d : number + } + } + if (isPrimitiveUnion(x)) { +>isPrimitiveUnion(x) : boolean +>isPrimitiveUnion : (x: unknown) => x is PrimitiveUnion +>x : unknown + + let a: number | string = x; +>a : PrimitiveUnion +>x : PrimitiveUnion + } + x // unknown +>x : unknown +} + +type A1 = { x: number }; +>A1 : A1 +>x : number + +type B1 = A1 & { kind: "B"; y: number }; +>B1 : B1 +>kind : "B" +>y : number + +type C1 = A1 & { kind: "C"; z: number }; +>C1 : C1 +>kind : "C" +>z : number + +function isBorC(a: A1): a is B1 | C1 { +>isBorC : (a: A1) => a is B1 | C1 +>a : A1 + + return (a as any).kind === "B" || (a as any).kind === "C"; +>(a as any).kind === "B" || (a as any).kind === "C" : boolean +>(a as any).kind === "B" : boolean +>(a as any).kind : any +>(a as any) : any +>a as any : any +>a : A1 +>kind : any +>"B" : "B" +>(a as any).kind === "C" : boolean +>(a as any).kind : any +>(a as any) : any +>a as any : any +>a : A1 +>kind : any +>"C" : "C" +} + +function isB1(a: A1): a is B1 { +>isB1 : (a: A1) => a is B1 +>a : A1 + + return (a as any).kind === "B"; +>(a as any).kind === "B" : boolean +>(a as any).kind : any +>(a as any) : any +>a as any : any +>a : A1 +>kind : any +>"B" : "B" +} + +function isC1(a: A1): a is C1 { +>isC1 : (a: A1) => a is C1 +>a : A1 + + return (a as any).kind === "C"; +>(a as any).kind === "C" : boolean +>(a as any).kind : any +>(a as any) : any +>a as any : any +>a : A1 +>kind : any +>"C" : "C" +} + +function fn1(a: A1) { +>fn1 : (a: A1) => void +>a : A1 + + if (isBorC(a)) { +>isBorC(a) : boolean +>isBorC : (a: A1) => a is B1 | C1 +>a : A1 + + if (a.kind === "B") { +>a.kind === "B" : boolean +>a.kind : "B" | "C" +>a : B1 | C1 +>kind : "B" | "C" +>"B" : "B" + + a.y; +>a.y : number +>a : B1 +>y : number + } + } +} + +function fn2(a: A1) { +>fn2 : (a: A1) => void +>a : A1 + + if (!isB1(a)) { +>!isB1(a) : boolean +>isB1(a) : boolean +>isB1 : (a: A1) => a is B1 +>a : A1 + + return; + } + if (!isC1(a)) { +>!isC1(a) : boolean +>isC1(a) : boolean +>isC1 : (a: A1) => a is C1 +>a : B1 + + if (a.kind === "B") { +>a.kind === "B" : boolean +>a.kind : "B" +>a : B1 +>kind : "B" +>"B" : "B" + + a.y; +>a.y : number +>a : B1 +>y : number + } + return; + } + if (a.kind === "B") { +>a.kind === "B" : boolean +>a.kind : never +>a : A1 & { kind: "B"; y: number; } & { kind: "C"; z: number; } +>kind : never +>"B" : "B" + + a.y; +>a.y : number +>a : A1 & { kind: "B"; y: number; } & { kind: "C"; z: number; } +>y : number + } +} + +declare function isTypeObj(x: unknown): x is { kind1: string, a?: number, b?: number }; +>isTypeObj : (x: unknown) => x is { kind1: string; a?: number; b?: number; } +>x : unknown +>kind1 : string +>a : number +>b : number + +declare function isTypeAB(x: unknown): x is { kind1: 'a', a: 1 } | { kind1: 'b', b: 2 }; +>isTypeAB : (x: unknown) => x is { kind1: "a"; a: 1; } | { kind1: "b"; b: 2; } +>x : unknown +>kind1 : "a" +>a : 1 +>kind1 : "b" +>b : 2 + +declare function isTypeCD(x: unknown): x is { kind2: 'c', c: 3 } | { kind2: 'd', d: 4 }; +>isTypeCD : (x: unknown) => x is { kind2: "c"; c: 3; } | { kind2: "d"; d: 4; } +>x : unknown +>kind2 : "c" +>c : 3 +>kind2 : "d" +>d : 4 + +function testComposition1(x: unknown) { +>testComposition1 : (x: unknown) => void +>x : unknown + + if (isTypeAB(x)) { +>isTypeAB(x) : boolean +>isTypeAB : (x: unknown) => x is { kind1: "a"; a: 1; } | { kind1: "b"; b: 2; } +>x : unknown + + if (isTypeCD(x)) { +>isTypeCD(x) : boolean +>isTypeCD : (x: unknown) => x is { kind2: "c"; c: 3; } | { kind2: "d"; d: 4; } +>x : { kind1: "a"; a: 1; } | { kind1: "b"; b: 2; } + + if (x.kind1 === 'a') { +>x.kind1 === 'a' : boolean +>x.kind1 : "a" | "b" +>x : ({ kind1: "a"; a: 1; } & { kind2: "c"; c: 3; }) | ({ kind1: "a"; a: 1; } & { kind2: "d"; d: 4; }) | ({ kind1: "b"; b: 2; } & { kind2: "c"; c: 3; }) | ({ kind1: "b"; b: 2; } & { kind2: "d"; d: 4; }) +>kind1 : "a" | "b" +>'a' : "a" + + x.a; +>x.a : 1 +>x : ({ kind1: "a"; a: 1; } & { kind2: "c"; c: 3; }) | ({ kind1: "a"; a: 1; } & { kind2: "d"; d: 4; }) +>a : 1 + } + if (x.kind2 === 'c') { +>x.kind2 === 'c' : boolean +>x.kind2 : "c" | "d" +>x : ({ kind1: "a"; a: 1; } & { kind2: "c"; c: 3; }) | ({ kind1: "a"; a: 1; } & { kind2: "d"; d: 4; }) | ({ kind1: "b"; b: 2; } & { kind2: "c"; c: 3; }) | ({ kind1: "b"; b: 2; } & { kind2: "d"; d: 4; }) +>kind2 : "c" | "d" +>'c' : "c" + + x.c; +>x.c : 3 +>x : ({ kind1: "a"; a: 1; } & { kind2: "c"; c: 3; }) | ({ kind1: "b"; b: 2; } & { kind2: "c"; c: 3; }) +>c : 3 + } + } + if (x.kind1 === 'a') { +>x.kind1 === 'a' : boolean +>x.kind1 : "a" | "b" +>x : { kind1: "a"; a: 1; } | { kind1: "b"; b: 2; } +>kind1 : "a" | "b" +>'a' : "a" + + x.a; +>x.a : 1 +>x : { kind1: "a"; a: 1; } +>a : 1 + } + if (x.kind2 === 'c') { +>x.kind2 === 'c' : boolean +>x.kind2 : any +>x : { kind1: "a"; a: 1; } | { kind1: "b"; b: 2; } +>kind2 : any +>'c' : "c" + + x.c; +>x.c : any +>x : { kind1: "a"; a: 1; } | { kind1: "b"; b: 2; } +>c : any + } + } +} + +function testComposition2(x: unknown) { +>testComposition2 : (x: unknown) => void +>x : unknown + + if (isTypeObj(x)) { +>isTypeObj(x) : boolean +>isTypeObj : (x: unknown) => x is { kind1: string; a?: number; b?: number; } +>x : unknown + + if (isTypeAB(x)) { +>isTypeAB(x) : boolean +>isTypeAB : (x: unknown) => x is { kind1: "a"; a: 1; } | { kind1: "b"; b: 2; } +>x : { kind1: string; a?: number; b?: number; } + + if (x.kind1 === "a") { +>x.kind1 === "a" : boolean +>x.kind1 : "a" | "b" +>x : { kind1: "a"; a: 1; } | { kind1: "b"; b: 2; } +>kind1 : "a" | "b" +>"a" : "a" + + x.a; +>x.a : 1 +>x : { kind1: "a"; a: 1; } +>a : 1 + } + } + if (x.kind1 === "a") { +>x.kind1 === "a" : boolean +>x.kind1 : string +>x : { kind1: string; a?: number; b?: number; } | { kind1: "a"; a: 1; } | { kind1: "b"; b: 2; } +>kind1 : string +>"a" : "a" + + x.a; // Error +>x.a : any +>x : { kind1: string; a?: number; b?: number; } | { kind1: "a"; a: 1; } | { kind1: "b"; b: 2; } +>a : any + } + } +} + +function testComposition3(x: unknown) { +>testComposition3 : (x: unknown) => void +>x : unknown + + if (isTypeAB(x)) { +>isTypeAB(x) : boolean +>isTypeAB : (x: unknown) => x is { kind1: "a"; a: 1; } | { kind1: "b"; b: 2; } +>x : unknown + + if (x.kind1 === 'a') { +>x.kind1 === 'a' : boolean +>x.kind1 : "a" | "b" +>x : { kind1: "a"; a: 1; } | { kind1: "b"; b: 2; } +>kind1 : "a" | "b" +>'a' : "a" + + x.a; +>x.a : 1 +>x : { kind1: "a"; a: 1; } +>a : 1 + } + return; + } + if (x.kind1 === 'a') { +>x.kind1 === 'a' : boolean +>x.kind1 : any +>x : unknown +>kind1 : any +>'a' : "a" + + x.a; // Error +>x.a : any +>x : unknown +>a : any + } + if (isTypeCD(x)) { +>isTypeCD(x) : boolean +>isTypeCD : (x: unknown) => x is { kind2: "c"; c: 3; } | { kind2: "d"; d: 4; } +>x : unknown + + if (x.kind2 === 'c') { +>x.kind2 === 'c' : boolean +>x.kind2 : "c" | "d" +>x : { kind2: "c"; c: 3; } | { kind2: "d"; d: 4; } +>kind2 : "c" | "d" +>'c' : "c" + + x.c; +>x.c : 3 +>x : { kind2: "c"; c: 3; } +>c : 3 + } + return; + } + if (x.kind2 === 'c') { +>x.kind2 === 'c' : boolean +>x.kind2 : any +>x : unknown +>kind2 : any +>'c' : "c" + + x.c; // Error +>x.c : any +>x : unknown +>c : any + } + if (isTypeAB(x)) { +>isTypeAB(x) : boolean +>isTypeAB : (x: unknown) => x is { kind1: "a"; a: 1; } | { kind1: "b"; b: 2; } +>x : unknown + + if (isTypeCD(x)) { +>isTypeCD(x) : boolean +>isTypeCD : (x: unknown) => x is { kind2: "c"; c: 3; } | { kind2: "d"; d: 4; } +>x : { kind1: "a"; a: 1; } | { kind1: "b"; b: 2; } + + if (x.kind1 === 'a') { +>x.kind1 === 'a' : boolean +>x.kind1 : "a" | "b" +>x : ({ kind1: "a"; a: 1; } & { kind2: "c"; c: 3; }) | ({ kind1: "a"; a: 1; } & { kind2: "d"; d: 4; }) | ({ kind1: "b"; b: 2; } & { kind2: "c"; c: 3; }) | ({ kind1: "b"; b: 2; } & { kind2: "d"; d: 4; }) +>kind1 : "a" | "b" +>'a' : "a" + + x.a; +>x.a : 1 +>x : ({ kind1: "a"; a: 1; } & { kind2: "c"; c: 3; }) | ({ kind1: "a"; a: 1; } & { kind2: "d"; d: 4; }) +>a : 1 + } + if (x.kind2 === 'c') { +>x.kind2 === 'c' : boolean +>x.kind2 : "c" | "d" +>x : ({ kind1: "a"; a: 1; } & { kind2: "c"; c: 3; }) | ({ kind1: "a"; a: 1; } & { kind2: "d"; d: 4; }) | ({ kind1: "b"; b: 2; } & { kind2: "c"; c: 3; }) | ({ kind1: "b"; b: 2; } & { kind2: "d"; d: 4; }) +>kind2 : "c" | "d" +>'c' : "c" + + x.c; +>x.c : 3 +>x : ({ kind1: "a"; a: 1; } & { kind2: "c"; c: 3; }) | ({ kind1: "b"; b: 2; } & { kind2: "c"; c: 3; }) +>c : 3 + } + } + } +} + +function looper(getter: () => unknown) { +>looper : (getter: () => unknown) => void +>getter : () => unknown + + let x = getter(); +>x : unknown +>getter() : unknown +>getter : () => unknown + + while (isTypeAB(x)) { +>isTypeAB(x) : boolean +>isTypeAB : (x: unknown) => x is { kind1: "a"; a: 1; } | { kind1: "b"; b: 2; } +>x : unknown + + if (isTypeCD(x)) { +>isTypeCD(x) : boolean +>isTypeCD : (x: unknown) => x is { kind2: "c"; c: 3; } | { kind2: "d"; d: 4; } +>x : { kind1: "a"; a: 1; } | { kind1: "b"; b: 2; } + + if (x.kind1 === 'a') { +>x.kind1 === 'a' : boolean +>x.kind1 : "a" | "b" +>x : ({ kind1: "a"; a: 1; } & { kind2: "c"; c: 3; }) | ({ kind1: "a"; a: 1; } & { kind2: "d"; d: 4; }) | ({ kind1: "b"; b: 2; } & { kind2: "c"; c: 3; }) | ({ kind1: "b"; b: 2; } & { kind2: "d"; d: 4; }) +>kind1 : "a" | "b" +>'a' : "a" + + x.a; +>x.a : 1 +>x : ({ kind1: "a"; a: 1; } & { kind2: "c"; c: 3; }) | ({ kind1: "a"; a: 1; } & { kind2: "d"; d: 4; }) +>a : 1 + } + if (x.kind2 === 'c') { +>x.kind2 === 'c' : boolean +>x.kind2 : "c" | "d" +>x : ({ kind1: "a"; a: 1; } & { kind2: "c"; c: 3; }) | ({ kind1: "a"; a: 1; } & { kind2: "d"; d: 4; }) | ({ kind1: "b"; b: 2; } & { kind2: "c"; c: 3; }) | ({ kind1: "b"; b: 2; } & { kind2: "d"; d: 4; }) +>kind2 : "c" | "d" +>'c' : "c" + + x.c; +>x.c : 3 +>x : ({ kind1: "a"; a: 1; } & { kind2: "c"; c: 3; }) | ({ kind1: "b"; b: 2; } & { kind2: "c"; c: 3; }) +>c : 3 + } + } + if (x.kind1 === 'a') { +>x.kind1 === 'a' : boolean +>x.kind1 : "a" | "b" +>x : { kind1: "a"; a: 1; } | { kind1: "b"; b: 2; } +>kind1 : "a" | "b" +>'a' : "a" + + x.a; +>x.a : 1 +>x : { kind1: "a"; a: 1; } +>a : 1 + } + if (x.kind2 === 'c') { +>x.kind2 === 'c' : boolean +>x.kind2 : any +>x : { kind1: "a"; a: 1; } | { kind1: "b"; b: 2; } +>kind2 : any +>'c' : "c" + + x.c; // Error +>x.c : any +>x : { kind1: "a"; a: 1; } | { kind1: "b"; b: 2; } +>c : any + } + } + if (x.kind1 === 'a') { +>x.kind1 === 'a' : boolean +>x.kind1 : any +>x : unknown +>kind1 : any +>'a' : "a" + + x.a; // error +>x.a : any +>x : unknown +>a : any + } +} + +interface Success { + success: true; +>success : true +>true : true + + response: object; +>response : object +} + +interface Error { + success: false; +>success : false +>false : false + + error: object; +>error : object +} + +function request(): Success | Error { +>request : () => Success | Error + + return null as any; +>null as any : any +>null : null +} + +// This does not work: +let r +>r : any + +r = request(); +>r = request() : Success | Error +>r : any +>request() : Success | Error +>request : () => Success | Error + +if (r.success) { +>r.success : any +>r : any +>success : any + + r.response; +>r.response : any +>r : any +>response : any +} + diff --git a/tests/cases/compiler/discriminantsAndTypePredicates.ts b/tests/cases/compiler/discriminantsAndTypePredicates.ts index c21ab7ec8f49a..842a3c772e931 100644 --- a/tests/cases/compiler/discriminantsAndTypePredicates.ts +++ b/tests/cases/compiler/discriminantsAndTypePredicates.ts @@ -28,4 +28,303 @@ function foo2(x: A | B): any { return x; // B } x; // never -} \ No newline at end of file +} + +// Repro from #30557 + +interface TypeA { + Name: "TypeA"; + Value1: "Cool stuff!"; +} + +interface TypeB { + Name: "TypeB"; + Value2: 0; +} + +type Type = TypeA | TypeB; + +declare function isType(x: unknown): x is Type; + +function WorksProperly(data: Type) { + if (data.Name === "TypeA") { + // TypeA + const value1 = data.Value1; + } +} + +function DoesNotWork(data: unknown) { + if (isType(data)) { + if (data.Name === "TypeA") { + // TypeA + const value1 = data.Value1; + } + } +} + +function narrowToNever(data: Type): "Cool stuff!" | 0 { + if (data.Name === "TypeA") { + return data.Value1; + } + if (data.Name === "TypeB") { + return data.Value2; + } + return data; +} + +function narrowToNeverUnknown(data: unknown): "Cool stuff!" | 0 { + if (isType(data)) { + if (data.Name === "TypeA") { + return data.Value1; + } + if (data.Name === "TypeB") { + return data.Value2; + } + return data; + } + throw "error"; +} + +type Foo = { kind: "a", a: number } | { kind: "b", b: number }; +type Bar = { kind: "c", c: number } | { kind: "d", d: number }; + +declare function isFoo(x: unknown): x is Foo; +declare function isBar(x: unknown): x is Bar; + +function blah(x: unknown) { + if (isFoo(x)) { + if (x.kind === "a") { + let a = x.a; + } + else if (x.kind === "b") { + let b = x.b; + } + } + else if (isBar(x)) { + if (x.kind === "c") { + let c = x.c; + } + else if (x.kind === "d") { + let d = x.d; + } + } + x // unknown +} + +type PrimitiveUnion = number | string +type FooComplex = { kind: "a", a: number } | { kind: "b", b: number } | number; +type BarComplex = { kind: "c", c: number } | { kind: "d", d: number } | string; + +declare function isPrimitiveUnion(x: unknown): x is PrimitiveUnion; +declare function isFooComplex(x: unknown): x is FooComplex; +declare function isBarComplex(x: unknown): x is BarComplex; +declare function isZZYYComplex(x: unknown): x is { kind: "z"; zzz: string } | { kind: "y", yyy: number }; + +function earlyExitsAndStuff(x: unknown) { + if (!isFooComplex(x) && !isBarComplex(x)) { + if (isZZYYComplex(x)) { + if (x.kind !== "z") { + return x.yyy; + } + return x.zzz; + } + return; + } + if (!!isPrimitiveUnion(x)) { + return x; + } + if (!isZZYYComplex(x)) { + if (x.kind === "a") { + let a = x.a; + } + if (x.kind === "b") { + let b = x.b; + } + if (x.kind === "c") { + let c = x.c; + } + if (x.kind === "d") { + let d = x.d; + } + } +} + +function bluergh(x: unknown) { + if (isPrimitiveUnion(x)) { + let a: number | string = x; + return; + } + if (isFooComplex(x) && typeof x === "object") { + if (x.kind === "a") { + let a = x.a; + } + else if (x.kind === "b") { + let b = x.b; + } + } + if (isPrimitiveUnion(x)) { + let a: number | string = x; + } + if (isBarComplex(x) && typeof x === "object") { + if (x.kind === "c") { + let c = x.c; + } + else if (x.kind === "d") { + let d = x.d; + } + } + if (isPrimitiveUnion(x)) { + let a: number | string = x; + } + x // unknown +} + +type A1 = { x: number }; +type B1 = A1 & { kind: "B"; y: number }; +type C1 = A1 & { kind: "C"; z: number }; + +function isBorC(a: A1): a is B1 | C1 { + return (a as any).kind === "B" || (a as any).kind === "C"; +} + +function isB1(a: A1): a is B1 { + return (a as any).kind === "B"; +} + +function isC1(a: A1): a is C1 { + return (a as any).kind === "C"; +} + +function fn1(a: A1) { + if (isBorC(a)) { + if (a.kind === "B") { + a.y; + } + } +} + +function fn2(a: A1) { + if (!isB1(a)) { + return; + } + if (!isC1(a)) { + if (a.kind === "B") { + a.y; + } + return; + } + if (a.kind === "B") { + a.y; + } +} + +declare function isTypeObj(x: unknown): x is { kind1: string, a?: number, b?: number }; +declare function isTypeAB(x: unknown): x is { kind1: 'a', a: 1 } | { kind1: 'b', b: 2 }; +declare function isTypeCD(x: unknown): x is { kind2: 'c', c: 3 } | { kind2: 'd', d: 4 }; + +function testComposition1(x: unknown) { + if (isTypeAB(x)) { + if (isTypeCD(x)) { + if (x.kind1 === 'a') { + x.a; + } + if (x.kind2 === 'c') { + x.c; + } + } + if (x.kind1 === 'a') { + x.a; + } + if (x.kind2 === 'c') { + x.c; + } + } +} + +function testComposition2(x: unknown) { + if (isTypeObj(x)) { + if (isTypeAB(x)) { + if (x.kind1 === "a") { + x.a; + } + } + if (x.kind1 === "a") { + x.a; // Error + } + } +} + +function testComposition3(x: unknown) { + if (isTypeAB(x)) { + if (x.kind1 === 'a') { + x.a; + } + return; + } + if (x.kind1 === 'a') { + x.a; // Error + } + if (isTypeCD(x)) { + if (x.kind2 === 'c') { + x.c; + } + return; + } + if (x.kind2 === 'c') { + x.c; // Error + } + if (isTypeAB(x)) { + if (isTypeCD(x)) { + if (x.kind1 === 'a') { + x.a; + } + if (x.kind2 === 'c') { + x.c; + } + } + } +} + +function looper(getter: () => unknown) { + let x = getter(); + while (isTypeAB(x)) { + if (isTypeCD(x)) { + if (x.kind1 === 'a') { + x.a; + } + if (x.kind2 === 'c') { + x.c; + } + } + if (x.kind1 === 'a') { + x.a; + } + if (x.kind2 === 'c') { + x.c; // Error + } + } + if (x.kind1 === 'a') { + x.a; // error + } +} + +interface Success { + success: true; + response: object; +} + +interface Error { + success: false; + error: object; +} + +function request(): Success | Error { + return null as any; +} + +// This does not work: +let r +r = request(); +if (r.success) { + r.response; +}