Skip to content

Commit b080aa9

Browse files
authored
Fix #16778 - use previous type and not declared type (#17381)
* Fix #16778 - use previous type to check discriminable type and not declared type * Rename prevType -> computedType
1 parent b9fe996 commit b080aa9

File tree

5 files changed

+300
-7
lines changed

5 files changed

+300
-7
lines changed

src/compiler/checker.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11500,7 +11500,7 @@ namespace ts {
1150011500
if (isMatchingReference(reference, expr)) {
1150111501
type = narrowTypeBySwitchOnDiscriminant(type, flow.switchStatement, flow.clauseStart, flow.clauseEnd);
1150211502
}
11503-
else if (isMatchingReferenceDiscriminant(expr)) {
11503+
else if (isMatchingReferenceDiscriminant(expr, type)) {
1150411504
type = narrowTypeByDiscriminant(type, <PropertyAccessExpression>expr, t => narrowTypeBySwitchOnDiscriminant(t, flow.switchStatement, flow.clauseStart, flow.clauseEnd));
1150511505
}
1150611506
return createFlowType(type, isIncomplete(flowType));
@@ -11620,11 +11620,11 @@ namespace ts {
1162011620
return result;
1162111621
}
1162211622

11623-
function isMatchingReferenceDiscriminant(expr: Expression) {
11623+
function isMatchingReferenceDiscriminant(expr: Expression, computedType: Type) {
1162411624
return expr.kind === SyntaxKind.PropertyAccessExpression &&
11625-
declaredType.flags & TypeFlags.Union &&
11625+
computedType.flags & TypeFlags.Union &&
1162611626
isMatchingReference(reference, (<PropertyAccessExpression>expr).expression) &&
11627-
isDiscriminantProperty(declaredType, (<PropertyAccessExpression>expr).name.escapedText);
11627+
isDiscriminantProperty(computedType, (<PropertyAccessExpression>expr).name.escapedText);
1162811628
}
1162911629

1163011630
function narrowTypeByDiscriminant(type: Type, propAccess: PropertyAccessExpression, narrowType: (t: Type) => Type): Type {
@@ -11638,7 +11638,7 @@ namespace ts {
1163811638
if (isMatchingReference(reference, expr)) {
1163911639
return getTypeWithFacts(type, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy);
1164011640
}
11641-
if (isMatchingReferenceDiscriminant(expr)) {
11641+
if (isMatchingReferenceDiscriminant(expr, declaredType)) {
1164211642
return narrowTypeByDiscriminant(type, <PropertyAccessExpression>expr, t => getTypeWithFacts(t, assumeTrue ? TypeFacts.Truthy : TypeFacts.Falsy));
1164311643
}
1164411644
if (containsMatchingReferenceDiscriminant(reference, expr)) {
@@ -11670,10 +11670,10 @@ namespace ts {
1167011670
if (isMatchingReference(reference, right)) {
1167111671
return narrowTypeByEquality(type, operator, left, assumeTrue);
1167211672
}
11673-
if (isMatchingReferenceDiscriminant(left)) {
11673+
if (isMatchingReferenceDiscriminant(left, declaredType)) {
1167411674
return narrowTypeByDiscriminant(type, <PropertyAccessExpression>left, t => narrowTypeByEquality(t, operator, right, assumeTrue));
1167511675
}
11676-
if (isMatchingReferenceDiscriminant(right)) {
11676+
if (isMatchingReferenceDiscriminant(right, declaredType)) {
1167711677
return narrowTypeByDiscriminant(type, <PropertyAccessExpression>right, t => narrowTypeByEquality(t, operator, left, assumeTrue));
1167811678
}
1167911679
if (containsMatchingReferenceDiscriminant(reference, left) || containsMatchingReferenceDiscriminant(reference, right)) {
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//// [flowControlTypeGuardThenSwitch.ts]
2+
enum Kind {
3+
A,
4+
B,
5+
}
6+
7+
interface Base {
8+
kind: Kind;
9+
}
10+
11+
interface A extends Base {
12+
kind: Kind.A;
13+
yar: any;
14+
}
15+
16+
interface B extends Base {
17+
kind: Kind.B;
18+
gar: any;
19+
}
20+
21+
type Both = A | B;
22+
function isBoth(x: Base): x is Both {
23+
return true;
24+
}
25+
26+
let foo: Base = undefined;
27+
if (isBoth(foo)) {
28+
switch (foo.kind) {
29+
case Kind.A:
30+
const myA: A = foo; // Should not be an error
31+
break;
32+
case Kind.B:
33+
const myB: B = foo;
34+
break;
35+
}
36+
}
37+
38+
39+
//// [flowControlTypeGuardThenSwitch.js]
40+
var Kind;
41+
(function (Kind) {
42+
Kind[Kind["A"] = 0] = "A";
43+
Kind[Kind["B"] = 1] = "B";
44+
})(Kind || (Kind = {}));
45+
function isBoth(x) {
46+
return true;
47+
}
48+
var foo = undefined;
49+
if (isBoth(foo)) {
50+
switch (foo.kind) {
51+
case Kind.A:
52+
var myA = foo; // Should not be an error
53+
break;
54+
case Kind.B:
55+
var myB = foo;
56+
break;
57+
}
58+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
=== tests/cases/compiler/flowControlTypeGuardThenSwitch.ts ===
2+
enum Kind {
3+
>Kind : Symbol(Kind, Decl(flowControlTypeGuardThenSwitch.ts, 0, 0))
4+
5+
A,
6+
>A : Symbol(Kind.A, Decl(flowControlTypeGuardThenSwitch.ts, 0, 11))
7+
8+
B,
9+
>B : Symbol(Kind.B, Decl(flowControlTypeGuardThenSwitch.ts, 1, 6))
10+
}
11+
12+
interface Base {
13+
>Base : Symbol(Base, Decl(flowControlTypeGuardThenSwitch.ts, 3, 1))
14+
15+
kind: Kind;
16+
>kind : Symbol(Base.kind, Decl(flowControlTypeGuardThenSwitch.ts, 5, 16))
17+
>Kind : Symbol(Kind, Decl(flowControlTypeGuardThenSwitch.ts, 0, 0))
18+
}
19+
20+
interface A extends Base {
21+
>A : Symbol(A, Decl(flowControlTypeGuardThenSwitch.ts, 7, 1))
22+
>Base : Symbol(Base, Decl(flowControlTypeGuardThenSwitch.ts, 3, 1))
23+
24+
kind: Kind.A;
25+
>kind : Symbol(A.kind, Decl(flowControlTypeGuardThenSwitch.ts, 9, 26))
26+
>Kind : Symbol(Kind, Decl(flowControlTypeGuardThenSwitch.ts, 0, 0))
27+
>A : Symbol(Kind.A, Decl(flowControlTypeGuardThenSwitch.ts, 0, 11))
28+
29+
yar: any;
30+
>yar : Symbol(A.yar, Decl(flowControlTypeGuardThenSwitch.ts, 10, 17))
31+
}
32+
33+
interface B extends Base {
34+
>B : Symbol(B, Decl(flowControlTypeGuardThenSwitch.ts, 12, 1))
35+
>Base : Symbol(Base, Decl(flowControlTypeGuardThenSwitch.ts, 3, 1))
36+
37+
kind: Kind.B;
38+
>kind : Symbol(B.kind, Decl(flowControlTypeGuardThenSwitch.ts, 14, 26))
39+
>Kind : Symbol(Kind, Decl(flowControlTypeGuardThenSwitch.ts, 0, 0))
40+
>B : Symbol(Kind.B, Decl(flowControlTypeGuardThenSwitch.ts, 1, 6))
41+
42+
gar: any;
43+
>gar : Symbol(B.gar, Decl(flowControlTypeGuardThenSwitch.ts, 15, 17))
44+
}
45+
46+
type Both = A | B;
47+
>Both : Symbol(Both, Decl(flowControlTypeGuardThenSwitch.ts, 17, 1))
48+
>A : Symbol(A, Decl(flowControlTypeGuardThenSwitch.ts, 7, 1))
49+
>B : Symbol(B, Decl(flowControlTypeGuardThenSwitch.ts, 12, 1))
50+
51+
function isBoth(x: Base): x is Both {
52+
>isBoth : Symbol(isBoth, Decl(flowControlTypeGuardThenSwitch.ts, 19, 18))
53+
>x : Symbol(x, Decl(flowControlTypeGuardThenSwitch.ts, 20, 16))
54+
>Base : Symbol(Base, Decl(flowControlTypeGuardThenSwitch.ts, 3, 1))
55+
>x : Symbol(x, Decl(flowControlTypeGuardThenSwitch.ts, 20, 16))
56+
>Both : Symbol(Both, Decl(flowControlTypeGuardThenSwitch.ts, 17, 1))
57+
58+
return true;
59+
}
60+
61+
let foo: Base = undefined;
62+
>foo : Symbol(foo, Decl(flowControlTypeGuardThenSwitch.ts, 24, 3))
63+
>Base : Symbol(Base, Decl(flowControlTypeGuardThenSwitch.ts, 3, 1))
64+
>undefined : Symbol(undefined)
65+
66+
if (isBoth(foo)) {
67+
>isBoth : Symbol(isBoth, Decl(flowControlTypeGuardThenSwitch.ts, 19, 18))
68+
>foo : Symbol(foo, Decl(flowControlTypeGuardThenSwitch.ts, 24, 3))
69+
70+
switch (foo.kind) {
71+
>foo.kind : Symbol(kind, Decl(flowControlTypeGuardThenSwitch.ts, 9, 26), Decl(flowControlTypeGuardThenSwitch.ts, 14, 26))
72+
>foo : Symbol(foo, Decl(flowControlTypeGuardThenSwitch.ts, 24, 3))
73+
>kind : Symbol(kind, Decl(flowControlTypeGuardThenSwitch.ts, 9, 26), Decl(flowControlTypeGuardThenSwitch.ts, 14, 26))
74+
75+
case Kind.A:
76+
>Kind.A : Symbol(Kind.A, Decl(flowControlTypeGuardThenSwitch.ts, 0, 11))
77+
>Kind : Symbol(Kind, Decl(flowControlTypeGuardThenSwitch.ts, 0, 0))
78+
>A : Symbol(Kind.A, Decl(flowControlTypeGuardThenSwitch.ts, 0, 11))
79+
80+
const myA: A = foo; // Should not be an error
81+
>myA : Symbol(myA, Decl(flowControlTypeGuardThenSwitch.ts, 28, 17))
82+
>A : Symbol(A, Decl(flowControlTypeGuardThenSwitch.ts, 7, 1))
83+
>foo : Symbol(foo, Decl(flowControlTypeGuardThenSwitch.ts, 24, 3))
84+
85+
break;
86+
case Kind.B:
87+
>Kind.B : Symbol(Kind.B, Decl(flowControlTypeGuardThenSwitch.ts, 1, 6))
88+
>Kind : Symbol(Kind, Decl(flowControlTypeGuardThenSwitch.ts, 0, 0))
89+
>B : Symbol(Kind.B, Decl(flowControlTypeGuardThenSwitch.ts, 1, 6))
90+
91+
const myB: B = foo;
92+
>myB : Symbol(myB, Decl(flowControlTypeGuardThenSwitch.ts, 31, 17))
93+
>B : Symbol(B, Decl(flowControlTypeGuardThenSwitch.ts, 12, 1))
94+
>foo : Symbol(foo, Decl(flowControlTypeGuardThenSwitch.ts, 24, 3))
95+
96+
break;
97+
}
98+
}
99+
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
=== tests/cases/compiler/flowControlTypeGuardThenSwitch.ts ===
2+
enum Kind {
3+
>Kind : Kind
4+
5+
A,
6+
>A : Kind.A
7+
8+
B,
9+
>B : Kind.B
10+
}
11+
12+
interface Base {
13+
>Base : Base
14+
15+
kind: Kind;
16+
>kind : Kind
17+
>Kind : Kind
18+
}
19+
20+
interface A extends Base {
21+
>A : A
22+
>Base : Base
23+
24+
kind: Kind.A;
25+
>kind : Kind.A
26+
>Kind : any
27+
>A : Kind.A
28+
29+
yar: any;
30+
>yar : any
31+
}
32+
33+
interface B extends Base {
34+
>B : B
35+
>Base : Base
36+
37+
kind: Kind.B;
38+
>kind : Kind.B
39+
>Kind : any
40+
>B : Kind.B
41+
42+
gar: any;
43+
>gar : any
44+
}
45+
46+
type Both = A | B;
47+
>Both : Both
48+
>A : A
49+
>B : B
50+
51+
function isBoth(x: Base): x is Both {
52+
>isBoth : (x: Base) => x is Both
53+
>x : Base
54+
>Base : Base
55+
>x : any
56+
>Both : Both
57+
58+
return true;
59+
>true : true
60+
}
61+
62+
let foo: Base = undefined;
63+
>foo : Base
64+
>Base : Base
65+
>undefined : undefined
66+
67+
if (isBoth(foo)) {
68+
>isBoth(foo) : boolean
69+
>isBoth : (x: Base) => x is Both
70+
>foo : Base
71+
72+
switch (foo.kind) {
73+
>foo.kind : Kind
74+
>foo : Both
75+
>kind : Kind
76+
77+
case Kind.A:
78+
>Kind.A : Kind.A
79+
>Kind : typeof Kind
80+
>A : Kind.A
81+
82+
const myA: A = foo; // Should not be an error
83+
>myA : A
84+
>A : A
85+
>foo : A
86+
87+
break;
88+
case Kind.B:
89+
>Kind.B : Kind.B
90+
>Kind : typeof Kind
91+
>B : Kind.B
92+
93+
const myB: B = foo;
94+
>myB : B
95+
>B : B
96+
>foo : B
97+
98+
break;
99+
}
100+
}
101+
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
enum Kind {
2+
A,
3+
B,
4+
}
5+
6+
interface Base {
7+
kind: Kind;
8+
}
9+
10+
interface A extends Base {
11+
kind: Kind.A;
12+
yar: any;
13+
}
14+
15+
interface B extends Base {
16+
kind: Kind.B;
17+
gar: any;
18+
}
19+
20+
type Both = A | B;
21+
function isBoth(x: Base): x is Both {
22+
return true;
23+
}
24+
25+
let foo: Base = undefined;
26+
if (isBoth(foo)) {
27+
switch (foo.kind) {
28+
case Kind.A:
29+
const myA: A = foo; // Should not be an error
30+
break;
31+
case Kind.B:
32+
const myB: B = foo;
33+
break;
34+
}
35+
}

0 commit comments

Comments
 (0)