Skip to content

Commit 557fc38

Browse files
authored
Merge pull request #10235 from Microsoft/fixDiscriminantWithNullableType
Fix discriminant type guards with nullable type
2 parents 3f1ec7a + 6c0bca0 commit 557fc38

File tree

5 files changed

+209
-8
lines changed

5 files changed

+209
-8
lines changed

src/compiler/checker.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7837,14 +7837,17 @@ namespace ts {
78377837
}
78387838

78397839
function isDiscriminantProperty(type: Type, name: string) {
7840-
if (type && type.flags & TypeFlags.Union) {
7841-
const prop = getPropertyOfType(type, name);
7842-
if (prop && prop.flags & SymbolFlags.SyntheticProperty) {
7843-
if ((<TransientSymbol>prop).isDiscriminantProperty === undefined) {
7844-
(<TransientSymbol>prop).isDiscriminantProperty = !(<TransientSymbol>prop).hasCommonType &&
7845-
isUnitUnionType(getTypeOfSymbol(prop));
7846-
}
7847-
return (<TransientSymbol>prop).isDiscriminantProperty;
7840+
if (type) {
7841+
const nonNullType = getNonNullableType(type);
7842+
if (nonNullType.flags & TypeFlags.Union) {
7843+
const prop = getPropertyOfType(nonNullType, name);
7844+
if (prop && prop.flags & SymbolFlags.SyntheticProperty) {
7845+
if ((<TransientSymbol>prop).isDiscriminantProperty === undefined) {
7846+
(<TransientSymbol>prop).isDiscriminantProperty = !(<TransientSymbol>prop).hasCommonType &&
7847+
isUnitUnionType(getTypeOfSymbol(prop));
7848+
}
7849+
return (<TransientSymbol>prop).isDiscriminantProperty;
7850+
}
78487851
}
78497852
}
78507853
return false;
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//// [discriminantsAndNullOrUndefined.ts]
2+
3+
// Repro from #10228
4+
5+
interface A { kind: 'A'; }
6+
interface B { kind: 'B'; }
7+
8+
type C = A | B | undefined;
9+
10+
function never(_: never): never {
11+
throw new Error();
12+
}
13+
14+
function useA(_: A): void { }
15+
function useB(_: B): void { }
16+
17+
declare var c: C;
18+
19+
if (c !== undefined) {
20+
switch (c.kind) {
21+
case 'A': useA(c); break;
22+
case 'B': useB(c); break;
23+
default: never(c);
24+
}
25+
}
26+
27+
//// [discriminantsAndNullOrUndefined.js]
28+
// Repro from #10228
29+
function never(_) {
30+
throw new Error();
31+
}
32+
function useA(_) { }
33+
function useB(_) { }
34+
if (c !== undefined) {
35+
switch (c.kind) {
36+
case 'A':
37+
useA(c);
38+
break;
39+
case 'B':
40+
useB(c);
41+
break;
42+
default: never(c);
43+
}
44+
}
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
=== tests/cases/compiler/discriminantsAndNullOrUndefined.ts ===
2+
3+
// Repro from #10228
4+
5+
interface A { kind: 'A'; }
6+
>A : Symbol(A, Decl(discriminantsAndNullOrUndefined.ts, 0, 0))
7+
>kind : Symbol(A.kind, Decl(discriminantsAndNullOrUndefined.ts, 3, 13))
8+
9+
interface B { kind: 'B'; }
10+
>B : Symbol(B, Decl(discriminantsAndNullOrUndefined.ts, 3, 26))
11+
>kind : Symbol(B.kind, Decl(discriminantsAndNullOrUndefined.ts, 4, 13))
12+
13+
type C = A | B | undefined;
14+
>C : Symbol(C, Decl(discriminantsAndNullOrUndefined.ts, 4, 26))
15+
>A : Symbol(A, Decl(discriminantsAndNullOrUndefined.ts, 0, 0))
16+
>B : Symbol(B, Decl(discriminantsAndNullOrUndefined.ts, 3, 26))
17+
18+
function never(_: never): never {
19+
>never : Symbol(never, Decl(discriminantsAndNullOrUndefined.ts, 6, 27))
20+
>_ : Symbol(_, Decl(discriminantsAndNullOrUndefined.ts, 8, 15))
21+
22+
throw new Error();
23+
>Error : Symbol(Error, Decl(lib.d.ts, --, --), Decl(lib.d.ts, --, --))
24+
}
25+
26+
function useA(_: A): void { }
27+
>useA : Symbol(useA, Decl(discriminantsAndNullOrUndefined.ts, 10, 1))
28+
>_ : Symbol(_, Decl(discriminantsAndNullOrUndefined.ts, 12, 14))
29+
>A : Symbol(A, Decl(discriminantsAndNullOrUndefined.ts, 0, 0))
30+
31+
function useB(_: B): void { }
32+
>useB : Symbol(useB, Decl(discriminantsAndNullOrUndefined.ts, 12, 29))
33+
>_ : Symbol(_, Decl(discriminantsAndNullOrUndefined.ts, 13, 14))
34+
>B : Symbol(B, Decl(discriminantsAndNullOrUndefined.ts, 3, 26))
35+
36+
declare var c: C;
37+
>c : Symbol(c, Decl(discriminantsAndNullOrUndefined.ts, 15, 11))
38+
>C : Symbol(C, Decl(discriminantsAndNullOrUndefined.ts, 4, 26))
39+
40+
if (c !== undefined) {
41+
>c : Symbol(c, Decl(discriminantsAndNullOrUndefined.ts, 15, 11))
42+
>undefined : Symbol(undefined)
43+
44+
switch (c.kind) {
45+
>c.kind : Symbol(kind, Decl(discriminantsAndNullOrUndefined.ts, 3, 13), Decl(discriminantsAndNullOrUndefined.ts, 4, 13))
46+
>c : Symbol(c, Decl(discriminantsAndNullOrUndefined.ts, 15, 11))
47+
>kind : Symbol(kind, Decl(discriminantsAndNullOrUndefined.ts, 3, 13), Decl(discriminantsAndNullOrUndefined.ts, 4, 13))
48+
49+
case 'A': useA(c); break;
50+
>useA : Symbol(useA, Decl(discriminantsAndNullOrUndefined.ts, 10, 1))
51+
>c : Symbol(c, Decl(discriminantsAndNullOrUndefined.ts, 15, 11))
52+
53+
case 'B': useB(c); break;
54+
>useB : Symbol(useB, Decl(discriminantsAndNullOrUndefined.ts, 12, 29))
55+
>c : Symbol(c, Decl(discriminantsAndNullOrUndefined.ts, 15, 11))
56+
57+
default: never(c);
58+
>never : Symbol(never, Decl(discriminantsAndNullOrUndefined.ts, 6, 27))
59+
>c : Symbol(c, Decl(discriminantsAndNullOrUndefined.ts, 15, 11))
60+
}
61+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
=== tests/cases/compiler/discriminantsAndNullOrUndefined.ts ===
2+
3+
// Repro from #10228
4+
5+
interface A { kind: 'A'; }
6+
>A : A
7+
>kind : "A"
8+
9+
interface B { kind: 'B'; }
10+
>B : B
11+
>kind : "B"
12+
13+
type C = A | B | undefined;
14+
>C : C
15+
>A : A
16+
>B : B
17+
18+
function never(_: never): never {
19+
>never : (_: never) => never
20+
>_ : never
21+
22+
throw new Error();
23+
>new Error() : Error
24+
>Error : ErrorConstructor
25+
}
26+
27+
function useA(_: A): void { }
28+
>useA : (_: A) => void
29+
>_ : A
30+
>A : A
31+
32+
function useB(_: B): void { }
33+
>useB : (_: B) => void
34+
>_ : B
35+
>B : B
36+
37+
declare var c: C;
38+
>c : C
39+
>C : C
40+
41+
if (c !== undefined) {
42+
>c !== undefined : boolean
43+
>c : C
44+
>undefined : undefined
45+
46+
switch (c.kind) {
47+
>c.kind : "A" | "B"
48+
>c : A | B
49+
>kind : "A" | "B"
50+
51+
case 'A': useA(c); break;
52+
>'A' : "A"
53+
>useA(c) : void
54+
>useA : (_: A) => void
55+
>c : A
56+
57+
case 'B': useB(c); break;
58+
>'B' : "B"
59+
>useB(c) : void
60+
>useB : (_: B) => void
61+
>c : B
62+
63+
default: never(c);
64+
>never(c) : never
65+
>never : (_: never) => never
66+
>c : never
67+
}
68+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// @strictNullChecks: true
2+
3+
// Repro from #10228
4+
5+
interface A { kind: 'A'; }
6+
interface B { kind: 'B'; }
7+
8+
type C = A | B | undefined;
9+
10+
function never(_: never): never {
11+
throw new Error();
12+
}
13+
14+
function useA(_: A): void { }
15+
function useB(_: B): void { }
16+
17+
declare var c: C;
18+
19+
if (c !== undefined) {
20+
switch (c.kind) {
21+
case 'A': useA(c); break;
22+
case 'B': useB(c); break;
23+
default: never(c);
24+
}
25+
}

0 commit comments

Comments
 (0)