diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3a6d58db31c..34c18f59d06 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -15780,9 +15780,6 @@ namespace ts { function getTypeAtSwitchClause(flow: FlowSwitchClause): FlowType { const expr = flow.switchStatement.expression; - if (containsMatchingReferenceDiscriminant(reference, expr)) { - return declaredType; - } const flowType = getTypeAtFlowNode(flow.antecedent); let type = getTypeFromFlowType(flowType); if (isMatchingReference(reference, expr)) { @@ -15797,6 +15794,9 @@ namespace ts { else if (expr.kind === SyntaxKind.TypeOfExpression && isMatchingReference(reference, (expr as TypeOfExpression).expression)) { type = narrowBySwitchOnTypeOf(type, flow.switchStatement, flow.clauseStart, flow.clauseEnd); } + else if (containsMatchingReferenceDiscriminant(reference, expr)) { + type = declaredType; + } return createFlowType(type, isIncomplete(flowType)); } diff --git a/tests/baselines/reference/discriminantPropertyCheck.errors.txt b/tests/baselines/reference/discriminantPropertyCheck.errors.txt index 313116edde5..962faaa740e 100644 --- a/tests/baselines/reference/discriminantPropertyCheck.errors.txt +++ b/tests/baselines/reference/discriminantPropertyCheck.errors.txt @@ -128,4 +128,58 @@ tests/cases/compiler/discriminantPropertyCheck.ts(65,9): error TS2532: Object is u.a && u.b && f(u.a, u.b); u.b && u.a && f(u.a, u.b); + + // Repro from #29012 + + type Additive = '+' | '-'; + type Multiplicative = '*' | '/'; + + interface AdditiveObj { + key: Additive + } + + interface MultiplicativeObj { + key: Multiplicative + } + + type Obj = AdditiveObj | MultiplicativeObj + + export function foo(obj: Obj) { + switch (obj.key) { + case '+': { + onlyPlus(obj.key); + return; + } + } + } + + function onlyPlus(arg: '+') { + return arg; + } + + // Repro from #29496 + + declare function never(value: never): never; + + const enum BarEnum { + bar1 = 1, + bar2 = 2, + } + + type UnionOfBar = TypeBar1 | TypeBar2; + type TypeBar1 = { type: BarEnum.bar1 }; + type TypeBar2 = { type: BarEnum.bar2 }; + + function func3(value: Partial) { + if (value.type !== undefined) { + switch (value.type) { + case BarEnum.bar1: + break; + case BarEnum.bar2: + break; + default: + never(value.type); + } + } + } \ No newline at end of file diff --git a/tests/baselines/reference/discriminantPropertyCheck.js b/tests/baselines/reference/discriminantPropertyCheck.js index 8b2c6f122bc..25e66e044cd 100644 --- a/tests/baselines/reference/discriminantPropertyCheck.js +++ b/tests/baselines/reference/discriminantPropertyCheck.js @@ -120,9 +120,65 @@ const u: U = {} as any; u.a && u.b && f(u.a, u.b); u.b && u.a && f(u.a, u.b); + +// Repro from #29012 + +type Additive = '+' | '-'; +type Multiplicative = '*' | '/'; + +interface AdditiveObj { + key: Additive +} + +interface MultiplicativeObj { + key: Multiplicative +} + +type Obj = AdditiveObj | MultiplicativeObj + +export function foo(obj: Obj) { + switch (obj.key) { + case '+': { + onlyPlus(obj.key); + return; + } + } +} + +function onlyPlus(arg: '+') { + return arg; +} + +// Repro from #29496 + +declare function never(value: never): never; + +const enum BarEnum { + bar1 = 1, + bar2 = 2, +} + +type UnionOfBar = TypeBar1 | TypeBar2; +type TypeBar1 = { type: BarEnum.bar1 }; +type TypeBar2 = { type: BarEnum.bar2 }; + +function func3(value: Partial) { + if (value.type !== undefined) { + switch (value.type) { + case BarEnum.bar1: + break; + case BarEnum.bar2: + break; + default: + never(value.type); + } + } +} //// [discriminantPropertyCheck.js] +"use strict"; +exports.__esModule = true; function goo1(x) { if (x.kind === "A" && x.foo !== undefined) { x.foo.length; @@ -188,3 +244,27 @@ var f = function (_a, _b) { }; var u = {}; u.a && u.b && f(u.a, u.b); u.b && u.a && f(u.a, u.b); +function foo(obj) { + switch (obj.key) { + case '+': { + onlyPlus(obj.key); + return; + } + } +} +exports.foo = foo; +function onlyPlus(arg) { + return arg; +} +function func3(value) { + if (value.type !== undefined) { + switch (value.type) { + case 1 /* bar1 */: + break; + case 2 /* bar2 */: + break; + default: + never(value.type); + } + } +} diff --git a/tests/baselines/reference/discriminantPropertyCheck.symbols b/tests/baselines/reference/discriminantPropertyCheck.symbols index 78884eaf69f..b02a8113d46 100644 --- a/tests/baselines/reference/discriminantPropertyCheck.symbols +++ b/tests/baselines/reference/discriminantPropertyCheck.symbols @@ -377,3 +377,134 @@ u.b && u.a && f(u.a, u.b); >u : Symbol(u, Decl(discriminantPropertyCheck.ts, 116, 5)) >b : Symbol(b, Decl(discriminantPropertyCheck.ts, 105, 13), Decl(discriminantPropertyCheck.ts, 110, 12)) +// Repro from #29012 + +type Additive = '+' | '-'; +>Additive : Symbol(Additive, Decl(discriminantPropertyCheck.ts, 120, 26)) + +type Multiplicative = '*' | '/'; +>Multiplicative : Symbol(Multiplicative, Decl(discriminantPropertyCheck.ts, 124, 26)) + +interface AdditiveObj { +>AdditiveObj : Symbol(AdditiveObj, Decl(discriminantPropertyCheck.ts, 125, 32)) + + key: Additive +>key : Symbol(AdditiveObj.key, Decl(discriminantPropertyCheck.ts, 127, 23)) +>Additive : Symbol(Additive, Decl(discriminantPropertyCheck.ts, 120, 26)) +} + +interface MultiplicativeObj { +>MultiplicativeObj : Symbol(MultiplicativeObj, Decl(discriminantPropertyCheck.ts, 129, 1)) + + key: Multiplicative +>key : Symbol(MultiplicativeObj.key, Decl(discriminantPropertyCheck.ts, 131, 29)) +>Multiplicative : Symbol(Multiplicative, Decl(discriminantPropertyCheck.ts, 124, 26)) +} + +type Obj = AdditiveObj | MultiplicativeObj +>Obj : Symbol(Obj, Decl(discriminantPropertyCheck.ts, 133, 1)) +>AdditiveObj : Symbol(AdditiveObj, Decl(discriminantPropertyCheck.ts, 125, 32)) +>MultiplicativeObj : Symbol(MultiplicativeObj, Decl(discriminantPropertyCheck.ts, 129, 1)) + +export function foo(obj: Obj) { +>foo : Symbol(foo, Decl(discriminantPropertyCheck.ts, 135, 42)) +>obj : Symbol(obj, Decl(discriminantPropertyCheck.ts, 137, 20)) +>Obj : Symbol(Obj, Decl(discriminantPropertyCheck.ts, 133, 1)) + + switch (obj.key) { +>obj.key : Symbol(key, Decl(discriminantPropertyCheck.ts, 127, 23), Decl(discriminantPropertyCheck.ts, 131, 29)) +>obj : Symbol(obj, Decl(discriminantPropertyCheck.ts, 137, 20)) +>key : Symbol(key, Decl(discriminantPropertyCheck.ts, 127, 23), Decl(discriminantPropertyCheck.ts, 131, 29)) + + case '+': { + onlyPlus(obj.key); +>onlyPlus : Symbol(onlyPlus, Decl(discriminantPropertyCheck.ts, 144, 1)) +>obj.key : Symbol(AdditiveObj.key, Decl(discriminantPropertyCheck.ts, 127, 23)) +>obj : Symbol(obj, Decl(discriminantPropertyCheck.ts, 137, 20)) +>key : Symbol(AdditiveObj.key, Decl(discriminantPropertyCheck.ts, 127, 23)) + + return; + } + } +} + +function onlyPlus(arg: '+') { +>onlyPlus : Symbol(onlyPlus, Decl(discriminantPropertyCheck.ts, 144, 1)) +>arg : Symbol(arg, Decl(discriminantPropertyCheck.ts, 146, 18)) + + return arg; +>arg : Symbol(arg, Decl(discriminantPropertyCheck.ts, 146, 18)) +} + +// Repro from #29496 + +declare function never(value: never): never; +>never : Symbol(never, Decl(discriminantPropertyCheck.ts, 148, 1)) +>value : Symbol(value, Decl(discriminantPropertyCheck.ts, 152, 23)) + +const enum BarEnum { +>BarEnum : Symbol(BarEnum, Decl(discriminantPropertyCheck.ts, 152, 44)) + + bar1 = 1, +>bar1 : Symbol(BarEnum.bar1, Decl(discriminantPropertyCheck.ts, 154, 20)) + + bar2 = 2, +>bar2 : Symbol(BarEnum.bar2, Decl(discriminantPropertyCheck.ts, 155, 13)) +} + +type UnionOfBar = TypeBar1 | TypeBar2; +>UnionOfBar : Symbol(UnionOfBar, Decl(discriminantPropertyCheck.ts, 157, 1)) +>TypeBar1 : Symbol(TypeBar1, Decl(discriminantPropertyCheck.ts, 159, 38)) +>TypeBar2 : Symbol(TypeBar2, Decl(discriminantPropertyCheck.ts, 160, 39)) + +type TypeBar1 = { type: BarEnum.bar1 }; +>TypeBar1 : Symbol(TypeBar1, Decl(discriminantPropertyCheck.ts, 159, 38)) +>type : Symbol(type, Decl(discriminantPropertyCheck.ts, 160, 17)) +>BarEnum : Symbol(BarEnum, Decl(discriminantPropertyCheck.ts, 152, 44)) +>bar1 : Symbol(BarEnum.bar1, Decl(discriminantPropertyCheck.ts, 154, 20)) + +type TypeBar2 = { type: BarEnum.bar2 }; +>TypeBar2 : Symbol(TypeBar2, Decl(discriminantPropertyCheck.ts, 160, 39)) +>type : Symbol(type, Decl(discriminantPropertyCheck.ts, 161, 17)) +>BarEnum : Symbol(BarEnum, Decl(discriminantPropertyCheck.ts, 152, 44)) +>bar2 : Symbol(BarEnum.bar2, Decl(discriminantPropertyCheck.ts, 155, 13)) + +function func3(value: Partial) { +>func3 : Symbol(func3, Decl(discriminantPropertyCheck.ts, 161, 39)) +>value : Symbol(value, Decl(discriminantPropertyCheck.ts, 163, 15)) +>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --)) +>UnionOfBar : Symbol(UnionOfBar, Decl(discriminantPropertyCheck.ts, 157, 1)) + + if (value.type !== undefined) { +>value.type : Symbol(type, Decl(discriminantPropertyCheck.ts, 160, 17), Decl(discriminantPropertyCheck.ts, 161, 17)) +>value : Symbol(value, Decl(discriminantPropertyCheck.ts, 163, 15)) +>type : Symbol(type, Decl(discriminantPropertyCheck.ts, 160, 17), Decl(discriminantPropertyCheck.ts, 161, 17)) +>undefined : Symbol(undefined) + + switch (value.type) { +>value.type : Symbol(type, Decl(discriminantPropertyCheck.ts, 160, 17), Decl(discriminantPropertyCheck.ts, 161, 17)) +>value : Symbol(value, Decl(discriminantPropertyCheck.ts, 163, 15)) +>type : Symbol(type, Decl(discriminantPropertyCheck.ts, 160, 17), Decl(discriminantPropertyCheck.ts, 161, 17)) + + case BarEnum.bar1: +>BarEnum.bar1 : Symbol(BarEnum.bar1, Decl(discriminantPropertyCheck.ts, 154, 20)) +>BarEnum : Symbol(BarEnum, Decl(discriminantPropertyCheck.ts, 152, 44)) +>bar1 : Symbol(BarEnum.bar1, Decl(discriminantPropertyCheck.ts, 154, 20)) + + break; + case BarEnum.bar2: +>BarEnum.bar2 : Symbol(BarEnum.bar2, Decl(discriminantPropertyCheck.ts, 155, 13)) +>BarEnum : Symbol(BarEnum, Decl(discriminantPropertyCheck.ts, 152, 44)) +>bar2 : Symbol(BarEnum.bar2, Decl(discriminantPropertyCheck.ts, 155, 13)) + + break; + default: + never(value.type); +>never : Symbol(never, Decl(discriminantPropertyCheck.ts, 148, 1)) +>value.type : Symbol(type, Decl(discriminantPropertyCheck.ts, 160, 17), Decl(discriminantPropertyCheck.ts, 161, 17)) +>value : Symbol(value, Decl(discriminantPropertyCheck.ts, 163, 15)) +>type : Symbol(type, Decl(discriminantPropertyCheck.ts, 160, 17), Decl(discriminantPropertyCheck.ts, 161, 17)) + } + } +} + diff --git a/tests/baselines/reference/discriminantPropertyCheck.types b/tests/baselines/reference/discriminantPropertyCheck.types index c243ef5e798..def89e7be1f 100644 --- a/tests/baselines/reference/discriminantPropertyCheck.types +++ b/tests/baselines/reference/discriminantPropertyCheck.types @@ -378,3 +378,126 @@ u.b && u.a && f(u.a, u.b); >u : U >b : string +// Repro from #29012 + +type Additive = '+' | '-'; +>Additive : Additive + +type Multiplicative = '*' | '/'; +>Multiplicative : Multiplicative + +interface AdditiveObj { + key: Additive +>key : Additive +} + +interface MultiplicativeObj { + key: Multiplicative +>key : Multiplicative +} + +type Obj = AdditiveObj | MultiplicativeObj +>Obj : Obj + +export function foo(obj: Obj) { +>foo : (obj: Obj) => void +>obj : Obj + + switch (obj.key) { +>obj.key : "+" | "-" | "*" | "/" +>obj : Obj +>key : "+" | "-" | "*" | "/" + + case '+': { +>'+' : "+" + + onlyPlus(obj.key); +>onlyPlus(obj.key) : "+" +>onlyPlus : (arg: "+") => "+" +>obj.key : "+" +>obj : AdditiveObj +>key : "+" + + return; + } + } +} + +function onlyPlus(arg: '+') { +>onlyPlus : (arg: "+") => "+" +>arg : "+" + + return arg; +>arg : "+" +} + +// Repro from #29496 + +declare function never(value: never): never; +>never : (value: never) => never +>value : never + +const enum BarEnum { +>BarEnum : BarEnum + + bar1 = 1, +>bar1 : BarEnum.bar1 +>1 : 1 + + bar2 = 2, +>bar2 : BarEnum.bar2 +>2 : 2 +} + +type UnionOfBar = TypeBar1 | TypeBar2; +>UnionOfBar : UnionOfBar + +type TypeBar1 = { type: BarEnum.bar1 }; +>TypeBar1 : TypeBar1 +>type : BarEnum.bar1 +>BarEnum : any + +type TypeBar2 = { type: BarEnum.bar2 }; +>TypeBar2 : TypeBar2 +>type : BarEnum.bar2 +>BarEnum : any + +function func3(value: Partial) { +>func3 : (value: Partial | Partial) => void +>value : Partial | Partial + + if (value.type !== undefined) { +>value.type !== undefined : boolean +>value.type : BarEnum | undefined +>value : Partial | Partial +>type : BarEnum | undefined +>undefined : undefined + + switch (value.type) { +>value.type : BarEnum +>value : Partial | Partial +>type : BarEnum + + case BarEnum.bar1: +>BarEnum.bar1 : BarEnum.bar1 +>BarEnum : typeof BarEnum +>bar1 : BarEnum.bar1 + + break; + case BarEnum.bar2: +>BarEnum.bar2 : BarEnum.bar2 +>BarEnum : typeof BarEnum +>bar2 : BarEnum.bar2 + + break; + default: + never(value.type); +>never(value.type) : never +>never : (value: never) => never +>value.type : never +>value : Partial | Partial +>type : never + } + } +} + diff --git a/tests/cases/compiler/discriminantPropertyCheck.ts b/tests/cases/compiler/discriminantPropertyCheck.ts index a24fe07973b..6a15af5db65 100644 --- a/tests/cases/compiler/discriminantPropertyCheck.ts +++ b/tests/cases/compiler/discriminantPropertyCheck.ts @@ -121,3 +121,57 @@ const u: U = {} as any; u.a && u.b && f(u.a, u.b); u.b && u.a && f(u.a, u.b); + +// Repro from #29012 + +type Additive = '+' | '-'; +type Multiplicative = '*' | '/'; + +interface AdditiveObj { + key: Additive +} + +interface MultiplicativeObj { + key: Multiplicative +} + +type Obj = AdditiveObj | MultiplicativeObj + +export function foo(obj: Obj) { + switch (obj.key) { + case '+': { + onlyPlus(obj.key); + return; + } + } +} + +function onlyPlus(arg: '+') { + return arg; +} + +// Repro from #29496 + +declare function never(value: never): never; + +const enum BarEnum { + bar1 = 1, + bar2 = 2, +} + +type UnionOfBar = TypeBar1 | TypeBar2; +type TypeBar1 = { type: BarEnum.bar1 }; +type TypeBar2 = { type: BarEnum.bar2 }; + +function func3(value: Partial) { + if (value.type !== undefined) { + switch (value.type) { + case BarEnum.bar1: + break; + case BarEnum.bar2: + break; + default: + never(value.type); + } + } +}