Skip to content

Commit 71d70ef

Browse files
authored
Merge pull request #29563 from Microsoft/fixSwitchControlFlow
Fix switch statement control flow logic
2 parents d782a54 + 936ee9b commit 71d70ef

File tree

6 files changed

+445
-3
lines changed

6 files changed

+445
-3
lines changed

src/compiler/checker.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15780,9 +15780,6 @@ namespace ts {
1578015780

1578115781
function getTypeAtSwitchClause(flow: FlowSwitchClause): FlowType {
1578215782
const expr = flow.switchStatement.expression;
15783-
if (containsMatchingReferenceDiscriminant(reference, expr)) {
15784-
return declaredType;
15785-
}
1578615783
const flowType = getTypeAtFlowNode(flow.antecedent);
1578715784
let type = getTypeFromFlowType(flowType);
1578815785
if (isMatchingReference(reference, expr)) {
@@ -15797,6 +15794,9 @@ namespace ts {
1579715794
else if (expr.kind === SyntaxKind.TypeOfExpression && isMatchingReference(reference, (expr as TypeOfExpression).expression)) {
1579815795
type = narrowBySwitchOnTypeOf(type, flow.switchStatement, flow.clauseStart, flow.clauseEnd);
1579915796
}
15797+
else if (containsMatchingReferenceDiscriminant(reference, expr)) {
15798+
type = declaredType;
15799+
}
1580015800
return createFlowType(type, isIncomplete(flowType));
1580115801
}
1580215802

tests/baselines/reference/discriminantPropertyCheck.errors.txt

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,4 +128,58 @@ tests/cases/compiler/discriminantPropertyCheck.ts(65,9): error TS2532: Object is
128128
u.a && u.b && f(u.a, u.b);
129129

130130
u.b && u.a && f(u.a, u.b);
131+
132+
// Repro from #29012
133+
134+
type Additive = '+' | '-';
135+
type Multiplicative = '*' | '/';
136+
137+
interface AdditiveObj {
138+
key: Additive
139+
}
140+
141+
interface MultiplicativeObj {
142+
key: Multiplicative
143+
}
144+
145+
type Obj = AdditiveObj | MultiplicativeObj
146+
147+
export function foo(obj: Obj) {
148+
switch (obj.key) {
149+
case '+': {
150+
onlyPlus(obj.key);
151+
return;
152+
}
153+
}
154+
}
155+
156+
function onlyPlus(arg: '+') {
157+
return arg;
158+
}
159+
160+
// Repro from #29496
161+
162+
declare function never(value: never): never;
163+
164+
const enum BarEnum {
165+
bar1 = 1,
166+
bar2 = 2,
167+
}
168+
169+
type UnionOfBar = TypeBar1 | TypeBar2;
170+
type TypeBar1 = { type: BarEnum.bar1 };
171+
type TypeBar2 = { type: BarEnum.bar2 };
172+
173+
function func3(value: Partial<UnionOfBar>) {
174+
if (value.type !== undefined) {
175+
switch (value.type) {
176+
case BarEnum.bar1:
177+
break;
178+
case BarEnum.bar2:
179+
break;
180+
default:
181+
never(value.type);
182+
}
183+
}
184+
}
131185

tests/baselines/reference/discriminantPropertyCheck.js

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,65 @@ const u: U = {} as any;
120120
u.a && u.b && f(u.a, u.b);
121121

122122
u.b && u.a && f(u.a, u.b);
123+
124+
// Repro from #29012
125+
126+
type Additive = '+' | '-';
127+
type Multiplicative = '*' | '/';
128+
129+
interface AdditiveObj {
130+
key: Additive
131+
}
132+
133+
interface MultiplicativeObj {
134+
key: Multiplicative
135+
}
136+
137+
type Obj = AdditiveObj | MultiplicativeObj
138+
139+
export function foo(obj: Obj) {
140+
switch (obj.key) {
141+
case '+': {
142+
onlyPlus(obj.key);
143+
return;
144+
}
145+
}
146+
}
147+
148+
function onlyPlus(arg: '+') {
149+
return arg;
150+
}
151+
152+
// Repro from #29496
153+
154+
declare function never(value: never): never;
155+
156+
const enum BarEnum {
157+
bar1 = 1,
158+
bar2 = 2,
159+
}
160+
161+
type UnionOfBar = TypeBar1 | TypeBar2;
162+
type TypeBar1 = { type: BarEnum.bar1 };
163+
type TypeBar2 = { type: BarEnum.bar2 };
164+
165+
function func3(value: Partial<UnionOfBar>) {
166+
if (value.type !== undefined) {
167+
switch (value.type) {
168+
case BarEnum.bar1:
169+
break;
170+
case BarEnum.bar2:
171+
break;
172+
default:
173+
never(value.type);
174+
}
175+
}
176+
}
123177

124178

125179
//// [discriminantPropertyCheck.js]
180+
"use strict";
181+
exports.__esModule = true;
126182
function goo1(x) {
127183
if (x.kind === "A" && x.foo !== undefined) {
128184
x.foo.length;
@@ -188,3 +244,27 @@ var f = function (_a, _b) { };
188244
var u = {};
189245
u.a && u.b && f(u.a, u.b);
190246
u.b && u.a && f(u.a, u.b);
247+
function foo(obj) {
248+
switch (obj.key) {
249+
case '+': {
250+
onlyPlus(obj.key);
251+
return;
252+
}
253+
}
254+
}
255+
exports.foo = foo;
256+
function onlyPlus(arg) {
257+
return arg;
258+
}
259+
function func3(value) {
260+
if (value.type !== undefined) {
261+
switch (value.type) {
262+
case 1 /* bar1 */:
263+
break;
264+
case 2 /* bar2 */:
265+
break;
266+
default:
267+
never(value.type);
268+
}
269+
}
270+
}

tests/baselines/reference/discriminantPropertyCheck.symbols

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,3 +377,134 @@ u.b && u.a && f(u.a, u.b);
377377
>u : Symbol(u, Decl(discriminantPropertyCheck.ts, 116, 5))
378378
>b : Symbol(b, Decl(discriminantPropertyCheck.ts, 105, 13), Decl(discriminantPropertyCheck.ts, 110, 12))
379379

380+
// Repro from #29012
381+
382+
type Additive = '+' | '-';
383+
>Additive : Symbol(Additive, Decl(discriminantPropertyCheck.ts, 120, 26))
384+
385+
type Multiplicative = '*' | '/';
386+
>Multiplicative : Symbol(Multiplicative, Decl(discriminantPropertyCheck.ts, 124, 26))
387+
388+
interface AdditiveObj {
389+
>AdditiveObj : Symbol(AdditiveObj, Decl(discriminantPropertyCheck.ts, 125, 32))
390+
391+
key: Additive
392+
>key : Symbol(AdditiveObj.key, Decl(discriminantPropertyCheck.ts, 127, 23))
393+
>Additive : Symbol(Additive, Decl(discriminantPropertyCheck.ts, 120, 26))
394+
}
395+
396+
interface MultiplicativeObj {
397+
>MultiplicativeObj : Symbol(MultiplicativeObj, Decl(discriminantPropertyCheck.ts, 129, 1))
398+
399+
key: Multiplicative
400+
>key : Symbol(MultiplicativeObj.key, Decl(discriminantPropertyCheck.ts, 131, 29))
401+
>Multiplicative : Symbol(Multiplicative, Decl(discriminantPropertyCheck.ts, 124, 26))
402+
}
403+
404+
type Obj = AdditiveObj | MultiplicativeObj
405+
>Obj : Symbol(Obj, Decl(discriminantPropertyCheck.ts, 133, 1))
406+
>AdditiveObj : Symbol(AdditiveObj, Decl(discriminantPropertyCheck.ts, 125, 32))
407+
>MultiplicativeObj : Symbol(MultiplicativeObj, Decl(discriminantPropertyCheck.ts, 129, 1))
408+
409+
export function foo(obj: Obj) {
410+
>foo : Symbol(foo, Decl(discriminantPropertyCheck.ts, 135, 42))
411+
>obj : Symbol(obj, Decl(discriminantPropertyCheck.ts, 137, 20))
412+
>Obj : Symbol(Obj, Decl(discriminantPropertyCheck.ts, 133, 1))
413+
414+
switch (obj.key) {
415+
>obj.key : Symbol(key, Decl(discriminantPropertyCheck.ts, 127, 23), Decl(discriminantPropertyCheck.ts, 131, 29))
416+
>obj : Symbol(obj, Decl(discriminantPropertyCheck.ts, 137, 20))
417+
>key : Symbol(key, Decl(discriminantPropertyCheck.ts, 127, 23), Decl(discriminantPropertyCheck.ts, 131, 29))
418+
419+
case '+': {
420+
onlyPlus(obj.key);
421+
>onlyPlus : Symbol(onlyPlus, Decl(discriminantPropertyCheck.ts, 144, 1))
422+
>obj.key : Symbol(AdditiveObj.key, Decl(discriminantPropertyCheck.ts, 127, 23))
423+
>obj : Symbol(obj, Decl(discriminantPropertyCheck.ts, 137, 20))
424+
>key : Symbol(AdditiveObj.key, Decl(discriminantPropertyCheck.ts, 127, 23))
425+
426+
return;
427+
}
428+
}
429+
}
430+
431+
function onlyPlus(arg: '+') {
432+
>onlyPlus : Symbol(onlyPlus, Decl(discriminantPropertyCheck.ts, 144, 1))
433+
>arg : Symbol(arg, Decl(discriminantPropertyCheck.ts, 146, 18))
434+
435+
return arg;
436+
>arg : Symbol(arg, Decl(discriminantPropertyCheck.ts, 146, 18))
437+
}
438+
439+
// Repro from #29496
440+
441+
declare function never(value: never): never;
442+
>never : Symbol(never, Decl(discriminantPropertyCheck.ts, 148, 1))
443+
>value : Symbol(value, Decl(discriminantPropertyCheck.ts, 152, 23))
444+
445+
const enum BarEnum {
446+
>BarEnum : Symbol(BarEnum, Decl(discriminantPropertyCheck.ts, 152, 44))
447+
448+
bar1 = 1,
449+
>bar1 : Symbol(BarEnum.bar1, Decl(discriminantPropertyCheck.ts, 154, 20))
450+
451+
bar2 = 2,
452+
>bar2 : Symbol(BarEnum.bar2, Decl(discriminantPropertyCheck.ts, 155, 13))
453+
}
454+
455+
type UnionOfBar = TypeBar1 | TypeBar2;
456+
>UnionOfBar : Symbol(UnionOfBar, Decl(discriminantPropertyCheck.ts, 157, 1))
457+
>TypeBar1 : Symbol(TypeBar1, Decl(discriminantPropertyCheck.ts, 159, 38))
458+
>TypeBar2 : Symbol(TypeBar2, Decl(discriminantPropertyCheck.ts, 160, 39))
459+
460+
type TypeBar1 = { type: BarEnum.bar1 };
461+
>TypeBar1 : Symbol(TypeBar1, Decl(discriminantPropertyCheck.ts, 159, 38))
462+
>type : Symbol(type, Decl(discriminantPropertyCheck.ts, 160, 17))
463+
>BarEnum : Symbol(BarEnum, Decl(discriminantPropertyCheck.ts, 152, 44))
464+
>bar1 : Symbol(BarEnum.bar1, Decl(discriminantPropertyCheck.ts, 154, 20))
465+
466+
type TypeBar2 = { type: BarEnum.bar2 };
467+
>TypeBar2 : Symbol(TypeBar2, Decl(discriminantPropertyCheck.ts, 160, 39))
468+
>type : Symbol(type, Decl(discriminantPropertyCheck.ts, 161, 17))
469+
>BarEnum : Symbol(BarEnum, Decl(discriminantPropertyCheck.ts, 152, 44))
470+
>bar2 : Symbol(BarEnum.bar2, Decl(discriminantPropertyCheck.ts, 155, 13))
471+
472+
function func3(value: Partial<UnionOfBar>) {
473+
>func3 : Symbol(func3, Decl(discriminantPropertyCheck.ts, 161, 39))
474+
>value : Symbol(value, Decl(discriminantPropertyCheck.ts, 163, 15))
475+
>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --))
476+
>UnionOfBar : Symbol(UnionOfBar, Decl(discriminantPropertyCheck.ts, 157, 1))
477+
478+
if (value.type !== undefined) {
479+
>value.type : Symbol(type, Decl(discriminantPropertyCheck.ts, 160, 17), Decl(discriminantPropertyCheck.ts, 161, 17))
480+
>value : Symbol(value, Decl(discriminantPropertyCheck.ts, 163, 15))
481+
>type : Symbol(type, Decl(discriminantPropertyCheck.ts, 160, 17), Decl(discriminantPropertyCheck.ts, 161, 17))
482+
>undefined : Symbol(undefined)
483+
484+
switch (value.type) {
485+
>value.type : Symbol(type, Decl(discriminantPropertyCheck.ts, 160, 17), Decl(discriminantPropertyCheck.ts, 161, 17))
486+
>value : Symbol(value, Decl(discriminantPropertyCheck.ts, 163, 15))
487+
>type : Symbol(type, Decl(discriminantPropertyCheck.ts, 160, 17), Decl(discriminantPropertyCheck.ts, 161, 17))
488+
489+
case BarEnum.bar1:
490+
>BarEnum.bar1 : Symbol(BarEnum.bar1, Decl(discriminantPropertyCheck.ts, 154, 20))
491+
>BarEnum : Symbol(BarEnum, Decl(discriminantPropertyCheck.ts, 152, 44))
492+
>bar1 : Symbol(BarEnum.bar1, Decl(discriminantPropertyCheck.ts, 154, 20))
493+
494+
break;
495+
case BarEnum.bar2:
496+
>BarEnum.bar2 : Symbol(BarEnum.bar2, Decl(discriminantPropertyCheck.ts, 155, 13))
497+
>BarEnum : Symbol(BarEnum, Decl(discriminantPropertyCheck.ts, 152, 44))
498+
>bar2 : Symbol(BarEnum.bar2, Decl(discriminantPropertyCheck.ts, 155, 13))
499+
500+
break;
501+
default:
502+
never(value.type);
503+
>never : Symbol(never, Decl(discriminantPropertyCheck.ts, 148, 1))
504+
>value.type : Symbol(type, Decl(discriminantPropertyCheck.ts, 160, 17), Decl(discriminantPropertyCheck.ts, 161, 17))
505+
>value : Symbol(value, Decl(discriminantPropertyCheck.ts, 163, 15))
506+
>type : Symbol(type, Decl(discriminantPropertyCheck.ts, 160, 17), Decl(discriminantPropertyCheck.ts, 161, 17))
507+
}
508+
}
509+
}
510+

0 commit comments

Comments
 (0)