diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 1ce1b7bfec6e8..292bfe51340db 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -1181,7 +1181,6 @@ namespace ts { } const preCaseLabel = createBranchLabel(); addAntecedent(preCaseLabel, createFlowSwitchClause(preSwitchCaseFlow!, node.parent, clauseStart, i + 1)); - addAntecedent(preCaseLabel, createFlowSwitchClause(preSwitchCaseFlow!, node.parent, clauseStart, i + 1)); addAntecedent(preCaseLabel, fallthroughFlow); currentFlow = finishFlowLabel(preCaseLabel); const clause = clauses[i]; diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 852bf39c16d77..c37066858a05d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -14972,9 +14972,12 @@ 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); - const expr = flow.switchStatement.expression; if (isMatchingReference(reference, expr)) { type = narrowTypeBySwitchOnDiscriminant(type, flow.switchStatement, flow.clauseStart, flow.clauseEnd); } diff --git a/tests/baselines/reference/discriminantPropertyCheck.errors.txt b/tests/baselines/reference/discriminantPropertyCheck.errors.txt index 33bdb6ea731ac..a57e17ed81ead 100644 --- a/tests/baselines/reference/discriminantPropertyCheck.errors.txt +++ b/tests/baselines/reference/discriminantPropertyCheck.errors.txt @@ -73,4 +73,37 @@ tests/cases/compiler/discriminantPropertyCheck.ts(65,9): error TS2532: Object is ~~~~~ !!! error TS2532: Object is possibly 'undefined'. } - } \ No newline at end of file + } + + // Repro from #27493 + + enum Types { Str = 1, Num = 2 } + + type Instance = StrType | NumType; + + interface StrType { + type: Types.Str; + value: string; + length: number; + } + + interface NumType { + type: Types.Num; + value: number; + } + + function func2(inst: Instance) { + while (true) { + switch (inst.type) { + case Types.Str: { + inst.value.length; + break; + } + case Types.Num: { + inst.value.toExponential; + break; + } + } + } + } + \ No newline at end of file diff --git a/tests/baselines/reference/discriminantPropertyCheck.js b/tests/baselines/reference/discriminantPropertyCheck.js index b946fe520494d..e58f28dc5a1c9 100644 --- a/tests/baselines/reference/discriminantPropertyCheck.js +++ b/tests/baselines/reference/discriminantPropertyCheck.js @@ -65,7 +65,40 @@ function foo6(x: Item) { if (x.foo !== undefined && x.qux) { x.foo.length; // Error, intervening discriminant guard } -} +} + +// Repro from #27493 + +enum Types { Str = 1, Num = 2 } + +type Instance = StrType | NumType; + +interface StrType { + type: Types.Str; + value: string; + length: number; +} + +interface NumType { + type: Types.Num; + value: number; +} + +function func2(inst: Instance) { + while (true) { + switch (inst.type) { + case Types.Str: { + inst.value.length; + break; + } + case Types.Num: { + inst.value.toExponential; + break; + } + } + } +} + //// [discriminantPropertyCheck.js] function goo1(x) { @@ -108,3 +141,23 @@ function foo6(x) { x.foo.length; // Error, intervening discriminant guard } } +// Repro from #27493 +var Types; +(function (Types) { + Types[Types["Str"] = 1] = "Str"; + Types[Types["Num"] = 2] = "Num"; +})(Types || (Types = {})); +function func2(inst) { + while (true) { + switch (inst.type) { + case Types.Str: { + inst.value.length; + break; + } + case Types.Num: { + inst.value.toExponential; + break; + } + } + } +} diff --git a/tests/baselines/reference/discriminantPropertyCheck.symbols b/tests/baselines/reference/discriminantPropertyCheck.symbols index 66ef653ac0d91..564cc55e3d362 100644 --- a/tests/baselines/reference/discriminantPropertyCheck.symbols +++ b/tests/baselines/reference/discriminantPropertyCheck.symbols @@ -228,3 +228,86 @@ function foo6(x: Item) { >length : Symbol(String.length, Decl(lib.es5.d.ts, --, --)) } } + +// Repro from #27493 + +enum Types { Str = 1, Num = 2 } +>Types : Symbol(Types, Decl(discriminantPropertyCheck.ts, 66, 1)) +>Str : Symbol(Types.Str, Decl(discriminantPropertyCheck.ts, 70, 12)) +>Num : Symbol(Types.Num, Decl(discriminantPropertyCheck.ts, 70, 21)) + +type Instance = StrType | NumType; +>Instance : Symbol(Instance, Decl(discriminantPropertyCheck.ts, 70, 31)) +>StrType : Symbol(StrType, Decl(discriminantPropertyCheck.ts, 72, 34)) +>NumType : Symbol(NumType, Decl(discriminantPropertyCheck.ts, 78, 1)) + +interface StrType { +>StrType : Symbol(StrType, Decl(discriminantPropertyCheck.ts, 72, 34)) + + type: Types.Str; +>type : Symbol(StrType.type, Decl(discriminantPropertyCheck.ts, 74, 19)) +>Types : Symbol(Types, Decl(discriminantPropertyCheck.ts, 66, 1)) +>Str : Symbol(Types.Str, Decl(discriminantPropertyCheck.ts, 70, 12)) + + value: string; +>value : Symbol(StrType.value, Decl(discriminantPropertyCheck.ts, 75, 20)) + + length: number; +>length : Symbol(StrType.length, Decl(discriminantPropertyCheck.ts, 76, 18)) +} + +interface NumType { +>NumType : Symbol(NumType, Decl(discriminantPropertyCheck.ts, 78, 1)) + + type: Types.Num; +>type : Symbol(NumType.type, Decl(discriminantPropertyCheck.ts, 80, 19)) +>Types : Symbol(Types, Decl(discriminantPropertyCheck.ts, 66, 1)) +>Num : Symbol(Types.Num, Decl(discriminantPropertyCheck.ts, 70, 21)) + + value: number; +>value : Symbol(NumType.value, Decl(discriminantPropertyCheck.ts, 81, 20)) +} + +function func2(inst: Instance) { +>func2 : Symbol(func2, Decl(discriminantPropertyCheck.ts, 83, 1)) +>inst : Symbol(inst, Decl(discriminantPropertyCheck.ts, 85, 15)) +>Instance : Symbol(Instance, Decl(discriminantPropertyCheck.ts, 70, 31)) + + while (true) { + switch (inst.type) { +>inst.type : Symbol(type, Decl(discriminantPropertyCheck.ts, 74, 19), Decl(discriminantPropertyCheck.ts, 80, 19)) +>inst : Symbol(inst, Decl(discriminantPropertyCheck.ts, 85, 15)) +>type : Symbol(type, Decl(discriminantPropertyCheck.ts, 74, 19), Decl(discriminantPropertyCheck.ts, 80, 19)) + + case Types.Str: { +>Types.Str : Symbol(Types.Str, Decl(discriminantPropertyCheck.ts, 70, 12)) +>Types : Symbol(Types, Decl(discriminantPropertyCheck.ts, 66, 1)) +>Str : Symbol(Types.Str, Decl(discriminantPropertyCheck.ts, 70, 12)) + + inst.value.length; +>inst.value.length : Symbol(String.length, Decl(lib.es5.d.ts, --, --)) +>inst.value : Symbol(StrType.value, Decl(discriminantPropertyCheck.ts, 75, 20)) +>inst : Symbol(inst, Decl(discriminantPropertyCheck.ts, 85, 15)) +>value : Symbol(StrType.value, Decl(discriminantPropertyCheck.ts, 75, 20)) +>length : Symbol(String.length, Decl(lib.es5.d.ts, --, --)) + + break; + } + case Types.Num: { +>Types.Num : Symbol(Types.Num, Decl(discriminantPropertyCheck.ts, 70, 21)) +>Types : Symbol(Types, Decl(discriminantPropertyCheck.ts, 66, 1)) +>Num : Symbol(Types.Num, Decl(discriminantPropertyCheck.ts, 70, 21)) + + inst.value.toExponential; +>inst.value.toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --)) +>inst.value : Symbol(NumType.value, Decl(discriminantPropertyCheck.ts, 81, 20)) +>inst : Symbol(inst, Decl(discriminantPropertyCheck.ts, 85, 15)) +>value : Symbol(NumType.value, Decl(discriminantPropertyCheck.ts, 81, 20)) +>toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --)) + + break; + } + } + } +} + diff --git a/tests/baselines/reference/discriminantPropertyCheck.types b/tests/baselines/reference/discriminantPropertyCheck.types index 9815433c09d37..4f34ceafc4385 100644 --- a/tests/baselines/reference/discriminantPropertyCheck.types +++ b/tests/baselines/reference/discriminantPropertyCheck.types @@ -232,3 +232,81 @@ function foo6(x: Item) { >length : number } } + +// Repro from #27493 + +enum Types { Str = 1, Num = 2 } +>Types : Types +>Str : Types.Str +>1 : 1 +>Num : Types.Num +>2 : 2 + +type Instance = StrType | NumType; +>Instance : Instance + +interface StrType { + type: Types.Str; +>type : Types.Str +>Types : any + + value: string; +>value : string + + length: number; +>length : number +} + +interface NumType { + type: Types.Num; +>type : Types.Num +>Types : any + + value: number; +>value : number +} + +function func2(inst: Instance) { +>func2 : (inst: Instance) => void +>inst : Instance + + while (true) { +>true : true + + switch (inst.type) { +>inst.type : Types +>inst : Instance +>type : Types + + case Types.Str: { +>Types.Str : Types.Str +>Types : typeof Types +>Str : Types.Str + + inst.value.length; +>inst.value.length : number +>inst.value : string +>inst : StrType +>value : string +>length : number + + break; + } + case Types.Num: { +>Types.Num : Types.Num +>Types : typeof Types +>Num : Types.Num + + inst.value.toExponential; +>inst.value.toExponential : (fractionDigits?: number | undefined) => string +>inst.value : number +>inst : NumType +>value : number +>toExponential : (fractionDigits?: number | undefined) => string + + break; + } + } + } +} + diff --git a/tests/cases/compiler/discriminantPropertyCheck.ts b/tests/cases/compiler/discriminantPropertyCheck.ts index e493f6bf77042..16fb847bdf019 100644 --- a/tests/cases/compiler/discriminantPropertyCheck.ts +++ b/tests/cases/compiler/discriminantPropertyCheck.ts @@ -66,4 +66,36 @@ function foo6(x: Item) { if (x.foo !== undefined && x.qux) { x.foo.length; // Error, intervening discriminant guard } -} \ No newline at end of file +} + +// Repro from #27493 + +enum Types { Str = 1, Num = 2 } + +type Instance = StrType | NumType; + +interface StrType { + type: Types.Str; + value: string; + length: number; +} + +interface NumType { + type: Types.Num; + value: number; +} + +function func2(inst: Instance) { + while (true) { + switch (inst.type) { + case Types.Str: { + inst.value.length; + break; + } + case Types.Num: { + inst.value.toExponential; + break; + } + } + } +}