Skip to content

Commit 2082ef2

Browse files
authored
fix narrowTypeByDiscriminant for non null expression access (#52136)
1 parent 280e3ac commit 2082ef2

File tree

6 files changed

+648
-2
lines changed

6 files changed

+648
-2
lines changed

src/compiler/checker.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -604,6 +604,7 @@ import {
604604
isNewExpression,
605605
isNightly,
606606
isNodeDescendantOf,
607+
isNonNullAccess,
607608
isNullishCoalesce,
608609
isNumericLiteral,
609610
isNumericLiteralName,
@@ -26372,12 +26373,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2637226373
if (propName === undefined) {
2637326374
return type;
2637426375
}
26375-
const removeNullable = strictNullChecks && isOptionalChain(access) && maybeTypeOfKind(type, TypeFlags.Nullable);
26376+
const optionalChain = isOptionalChain(access);
26377+
const removeNullable = strictNullChecks && (optionalChain || isNonNullAccess(access)) && maybeTypeOfKind(type, TypeFlags.Nullable);
2637626378
let propType = getTypeOfPropertyOfType(removeNullable ? getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull) : type, propName);
2637726379
if (!propType) {
2637826380
return type;
2637926381
}
26380-
propType = removeNullable ? getOptionalType(propType) : propType;
26382+
propType = removeNullable && optionalChain ? getOptionalType(propType) : propType;
2638126383
const narrowedPropType = narrowType(propType);
2638226384
return filterType(type, t => {
2638326385
const discriminantType = getTypeOfPropertyOrIndexSignature(t, propName);

src/compiler/utilities.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,6 +295,7 @@ import {
295295
isNamespaceExport,
296296
isNamespaceExportDeclaration,
297297
isNamespaceImport,
298+
isNonNullExpression,
298299
isNoSubstitutionTemplateLiteral,
299300
isNumericLiteral,
300301
isObjectLiteralExpression,
@@ -9481,3 +9482,10 @@ export function isOptionalDeclaration(declaration: Declaration): boolean {
94819482
return false;
94829483
}
94839484
}
9485+
9486+
/** @internal */
9487+
export function isNonNullAccess(node: Node): node is AccessExpression {
9488+
const kind = node.kind;
9489+
return (kind === SyntaxKind.PropertyAccessExpression
9490+
|| kind === SyntaxKind.ElementAccessExpression) && isNonNullExpression((node as AccessExpression).expression);
9491+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
//// [narrowingUnionWithBang.ts]
2+
type WorkingType = {
3+
thing?:
4+
{ name: 'Error1', message: string } |
5+
{ name: 'Error2', message: string } |
6+
{ name: 'Error3', message: string } |
7+
{ name: 'Error4', message: string } |
8+
{ name: 'Error5', message: string } |
9+
{ name: 'Error6', message: string } |
10+
{ name: 'Error7', message: string } |
11+
{ name: 'Error8', message: string } |
12+
{ name: 'Error9', message: string } |
13+
{ name: 'Correct', id: string }
14+
};
15+
const working: WorkingType = null as unknown as WorkingType;
16+
if (working.thing!.name !== "Correct") {
17+
console.log(working.thing!.message)
18+
} else {
19+
console.log(working.thing!.id);
20+
}
21+
22+
type BorkedType = {
23+
thing?:
24+
{ name: 'Error1', message: string } |
25+
{ name: 'Error2', message: string } |
26+
{ name: 'Error3', message: string } |
27+
{ name: 'Error4', message: string } |
28+
{ name: 'Error5', message: string } |
29+
{ name: 'Error6', message: string } |
30+
{ name: 'Error7', message: string } |
31+
{ name: 'Error8', message: string } |
32+
{ name: 'Correct', id: string }
33+
};
34+
const borked: BorkedType = null as unknown as BorkedType;
35+
if (borked.thing!.name !== "Correct") {
36+
console.log(borked.thing!.message);
37+
} else {
38+
console.log(borked.thing!.id);
39+
}
40+
41+
export type FixedType = {
42+
thing?:
43+
{ name: 'Error1', message: string } |
44+
{ name: 'Error2', message: string } |
45+
{ name: 'Error3', message: string } |
46+
{ name: 'Error4', message: string } |
47+
{ name: 'Error5', message: string } |
48+
{ name: 'Error6', message: string } |
49+
{ name: 'Error7', message: string } |
50+
{ name: 'Error8', message: string } |
51+
{ name: 'Correct', id: string }
52+
};
53+
const fixed: FixedType = null as unknown as FixedType;
54+
55+
if (fixed.thing?.name !== "Correct") {
56+
console.log(fixed.thing!.message);
57+
} else {
58+
console.log(fixed.thing.id);
59+
}
60+
61+
//// [narrowingUnionWithBang.js]
62+
"use strict";
63+
var _a;
64+
Object.defineProperty(exports, "__esModule", { value: true });
65+
var working = null;
66+
if (working.thing.name !== "Correct") {
67+
console.log(working.thing.message);
68+
}
69+
else {
70+
console.log(working.thing.id);
71+
}
72+
var borked = null;
73+
if (borked.thing.name !== "Correct") {
74+
console.log(borked.thing.message);
75+
}
76+
else {
77+
console.log(borked.thing.id);
78+
}
79+
var fixed = null;
80+
if (((_a = fixed.thing) === null || _a === void 0 ? void 0 : _a.name) !== "Correct") {
81+
console.log(fixed.thing.message);
82+
}
83+
else {
84+
console.log(fixed.thing.id);
85+
}
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
=== tests/cases/compiler/narrowingUnionWithBang.ts ===
2+
type WorkingType = {
3+
>WorkingType : Symbol(WorkingType, Decl(narrowingUnionWithBang.ts, 0, 0))
4+
5+
thing?:
6+
>thing : Symbol(thing, Decl(narrowingUnionWithBang.ts, 0, 20))
7+
8+
{ name: 'Error1', message: string } |
9+
>name : Symbol(name, Decl(narrowingUnionWithBang.ts, 2, 5))
10+
>message : Symbol(message, Decl(narrowingUnionWithBang.ts, 2, 21))
11+
12+
{ name: 'Error2', message: string } |
13+
>name : Symbol(name, Decl(narrowingUnionWithBang.ts, 3, 5))
14+
>message : Symbol(message, Decl(narrowingUnionWithBang.ts, 3, 21))
15+
16+
{ name: 'Error3', message: string } |
17+
>name : Symbol(name, Decl(narrowingUnionWithBang.ts, 4, 5))
18+
>message : Symbol(message, Decl(narrowingUnionWithBang.ts, 4, 21))
19+
20+
{ name: 'Error4', message: string } |
21+
>name : Symbol(name, Decl(narrowingUnionWithBang.ts, 5, 5))
22+
>message : Symbol(message, Decl(narrowingUnionWithBang.ts, 5, 21))
23+
24+
{ name: 'Error5', message: string } |
25+
>name : Symbol(name, Decl(narrowingUnionWithBang.ts, 6, 5))
26+
>message : Symbol(message, Decl(narrowingUnionWithBang.ts, 6, 21))
27+
28+
{ name: 'Error6', message: string } |
29+
>name : Symbol(name, Decl(narrowingUnionWithBang.ts, 7, 5))
30+
>message : Symbol(message, Decl(narrowingUnionWithBang.ts, 7, 21))
31+
32+
{ name: 'Error7', message: string } |
33+
>name : Symbol(name, Decl(narrowingUnionWithBang.ts, 8, 5))
34+
>message : Symbol(message, Decl(narrowingUnionWithBang.ts, 8, 21))
35+
36+
{ name: 'Error8', message: string } |
37+
>name : Symbol(name, Decl(narrowingUnionWithBang.ts, 9, 5))
38+
>message : Symbol(message, Decl(narrowingUnionWithBang.ts, 9, 21))
39+
40+
{ name: 'Error9', message: string } |
41+
>name : Symbol(name, Decl(narrowingUnionWithBang.ts, 10, 5))
42+
>message : Symbol(message, Decl(narrowingUnionWithBang.ts, 10, 21))
43+
44+
{ name: 'Correct', id: string }
45+
>name : Symbol(name, Decl(narrowingUnionWithBang.ts, 11, 5))
46+
>id : Symbol(id, Decl(narrowingUnionWithBang.ts, 11, 22))
47+
48+
};
49+
const working: WorkingType = null as unknown as WorkingType;
50+
>working : Symbol(working, Decl(narrowingUnionWithBang.ts, 13, 5))
51+
>WorkingType : Symbol(WorkingType, Decl(narrowingUnionWithBang.ts, 0, 0))
52+
>WorkingType : Symbol(WorkingType, Decl(narrowingUnionWithBang.ts, 0, 0))
53+
54+
if (working.thing!.name !== "Correct") {
55+
>working.thing!.name : Symbol(name, Decl(narrowingUnionWithBang.ts, 2, 5), Decl(narrowingUnionWithBang.ts, 3, 5), Decl(narrowingUnionWithBang.ts, 4, 5), Decl(narrowingUnionWithBang.ts, 5, 5), Decl(narrowingUnionWithBang.ts, 6, 5) ... and 5 more)
56+
>working.thing : Symbol(thing, Decl(narrowingUnionWithBang.ts, 0, 20))
57+
>working : Symbol(working, Decl(narrowingUnionWithBang.ts, 13, 5))
58+
>thing : Symbol(thing, Decl(narrowingUnionWithBang.ts, 0, 20))
59+
>name : Symbol(name, Decl(narrowingUnionWithBang.ts, 2, 5), Decl(narrowingUnionWithBang.ts, 3, 5), Decl(narrowingUnionWithBang.ts, 4, 5), Decl(narrowingUnionWithBang.ts, 5, 5), Decl(narrowingUnionWithBang.ts, 6, 5) ... and 5 more)
60+
61+
console.log(working.thing!.message)
62+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
63+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
64+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
65+
>working.thing!.message : Symbol(message, Decl(narrowingUnionWithBang.ts, 2, 21), Decl(narrowingUnionWithBang.ts, 3, 21), Decl(narrowingUnionWithBang.ts, 4, 21), Decl(narrowingUnionWithBang.ts, 5, 21), Decl(narrowingUnionWithBang.ts, 6, 21) ... and 4 more)
66+
>working.thing : Symbol(thing, Decl(narrowingUnionWithBang.ts, 0, 20))
67+
>working : Symbol(working, Decl(narrowingUnionWithBang.ts, 13, 5))
68+
>thing : Symbol(thing, Decl(narrowingUnionWithBang.ts, 0, 20))
69+
>message : Symbol(message, Decl(narrowingUnionWithBang.ts, 2, 21), Decl(narrowingUnionWithBang.ts, 3, 21), Decl(narrowingUnionWithBang.ts, 4, 21), Decl(narrowingUnionWithBang.ts, 5, 21), Decl(narrowingUnionWithBang.ts, 6, 21) ... and 4 more)
70+
71+
} else {
72+
console.log(working.thing!.id);
73+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
74+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
75+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
76+
>working.thing!.id : Symbol(id, Decl(narrowingUnionWithBang.ts, 11, 22))
77+
>working.thing : Symbol(thing, Decl(narrowingUnionWithBang.ts, 0, 20))
78+
>working : Symbol(working, Decl(narrowingUnionWithBang.ts, 13, 5))
79+
>thing : Symbol(thing, Decl(narrowingUnionWithBang.ts, 0, 20))
80+
>id : Symbol(id, Decl(narrowingUnionWithBang.ts, 11, 22))
81+
}
82+
83+
type BorkedType = {
84+
>BorkedType : Symbol(BorkedType, Decl(narrowingUnionWithBang.ts, 18, 1))
85+
86+
thing?:
87+
>thing : Symbol(thing, Decl(narrowingUnionWithBang.ts, 20, 19))
88+
89+
{ name: 'Error1', message: string } |
90+
>name : Symbol(name, Decl(narrowingUnionWithBang.ts, 22, 5))
91+
>message : Symbol(message, Decl(narrowingUnionWithBang.ts, 22, 21))
92+
93+
{ name: 'Error2', message: string } |
94+
>name : Symbol(name, Decl(narrowingUnionWithBang.ts, 23, 5))
95+
>message : Symbol(message, Decl(narrowingUnionWithBang.ts, 23, 21))
96+
97+
{ name: 'Error3', message: string } |
98+
>name : Symbol(name, Decl(narrowingUnionWithBang.ts, 24, 5))
99+
>message : Symbol(message, Decl(narrowingUnionWithBang.ts, 24, 21))
100+
101+
{ name: 'Error4', message: string } |
102+
>name : Symbol(name, Decl(narrowingUnionWithBang.ts, 25, 5))
103+
>message : Symbol(message, Decl(narrowingUnionWithBang.ts, 25, 21))
104+
105+
{ name: 'Error5', message: string } |
106+
>name : Symbol(name, Decl(narrowingUnionWithBang.ts, 26, 5))
107+
>message : Symbol(message, Decl(narrowingUnionWithBang.ts, 26, 21))
108+
109+
{ name: 'Error6', message: string } |
110+
>name : Symbol(name, Decl(narrowingUnionWithBang.ts, 27, 5))
111+
>message : Symbol(message, Decl(narrowingUnionWithBang.ts, 27, 21))
112+
113+
{ name: 'Error7', message: string } |
114+
>name : Symbol(name, Decl(narrowingUnionWithBang.ts, 28, 5))
115+
>message : Symbol(message, Decl(narrowingUnionWithBang.ts, 28, 21))
116+
117+
{ name: 'Error8', message: string } |
118+
>name : Symbol(name, Decl(narrowingUnionWithBang.ts, 29, 5))
119+
>message : Symbol(message, Decl(narrowingUnionWithBang.ts, 29, 21))
120+
121+
{ name: 'Correct', id: string }
122+
>name : Symbol(name, Decl(narrowingUnionWithBang.ts, 30, 5))
123+
>id : Symbol(id, Decl(narrowingUnionWithBang.ts, 30, 22))
124+
125+
};
126+
const borked: BorkedType = null as unknown as BorkedType;
127+
>borked : Symbol(borked, Decl(narrowingUnionWithBang.ts, 32, 5))
128+
>BorkedType : Symbol(BorkedType, Decl(narrowingUnionWithBang.ts, 18, 1))
129+
>BorkedType : Symbol(BorkedType, Decl(narrowingUnionWithBang.ts, 18, 1))
130+
131+
if (borked.thing!.name !== "Correct") {
132+
>borked.thing!.name : Symbol(name, Decl(narrowingUnionWithBang.ts, 22, 5), Decl(narrowingUnionWithBang.ts, 23, 5), Decl(narrowingUnionWithBang.ts, 24, 5), Decl(narrowingUnionWithBang.ts, 25, 5), Decl(narrowingUnionWithBang.ts, 26, 5) ... and 4 more)
133+
>borked.thing : Symbol(thing, Decl(narrowingUnionWithBang.ts, 20, 19))
134+
>borked : Symbol(borked, Decl(narrowingUnionWithBang.ts, 32, 5))
135+
>thing : Symbol(thing, Decl(narrowingUnionWithBang.ts, 20, 19))
136+
>name : Symbol(name, Decl(narrowingUnionWithBang.ts, 22, 5), Decl(narrowingUnionWithBang.ts, 23, 5), Decl(narrowingUnionWithBang.ts, 24, 5), Decl(narrowingUnionWithBang.ts, 25, 5), Decl(narrowingUnionWithBang.ts, 26, 5) ... and 4 more)
137+
138+
console.log(borked.thing!.message);
139+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
140+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
141+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
142+
>borked.thing!.message : Symbol(message, Decl(narrowingUnionWithBang.ts, 22, 21), Decl(narrowingUnionWithBang.ts, 23, 21), Decl(narrowingUnionWithBang.ts, 24, 21), Decl(narrowingUnionWithBang.ts, 25, 21), Decl(narrowingUnionWithBang.ts, 26, 21) ... and 3 more)
143+
>borked.thing : Symbol(thing, Decl(narrowingUnionWithBang.ts, 20, 19))
144+
>borked : Symbol(borked, Decl(narrowingUnionWithBang.ts, 32, 5))
145+
>thing : Symbol(thing, Decl(narrowingUnionWithBang.ts, 20, 19))
146+
>message : Symbol(message, Decl(narrowingUnionWithBang.ts, 22, 21), Decl(narrowingUnionWithBang.ts, 23, 21), Decl(narrowingUnionWithBang.ts, 24, 21), Decl(narrowingUnionWithBang.ts, 25, 21), Decl(narrowingUnionWithBang.ts, 26, 21) ... and 3 more)
147+
148+
} else {
149+
console.log(borked.thing!.id);
150+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
151+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
152+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
153+
>borked.thing!.id : Symbol(id, Decl(narrowingUnionWithBang.ts, 30, 22))
154+
>borked.thing : Symbol(thing, Decl(narrowingUnionWithBang.ts, 20, 19))
155+
>borked : Symbol(borked, Decl(narrowingUnionWithBang.ts, 32, 5))
156+
>thing : Symbol(thing, Decl(narrowingUnionWithBang.ts, 20, 19))
157+
>id : Symbol(id, Decl(narrowingUnionWithBang.ts, 30, 22))
158+
}
159+
160+
export type FixedType = {
161+
>FixedType : Symbol(FixedType, Decl(narrowingUnionWithBang.ts, 37, 1))
162+
163+
thing?:
164+
>thing : Symbol(thing, Decl(narrowingUnionWithBang.ts, 39, 25))
165+
166+
{ name: 'Error1', message: string } |
167+
>name : Symbol(name, Decl(narrowingUnionWithBang.ts, 41, 5))
168+
>message : Symbol(message, Decl(narrowingUnionWithBang.ts, 41, 21))
169+
170+
{ name: 'Error2', message: string } |
171+
>name : Symbol(name, Decl(narrowingUnionWithBang.ts, 42, 5))
172+
>message : Symbol(message, Decl(narrowingUnionWithBang.ts, 42, 21))
173+
174+
{ name: 'Error3', message: string } |
175+
>name : Symbol(name, Decl(narrowingUnionWithBang.ts, 43, 5))
176+
>message : Symbol(message, Decl(narrowingUnionWithBang.ts, 43, 21))
177+
178+
{ name: 'Error4', message: string } |
179+
>name : Symbol(name, Decl(narrowingUnionWithBang.ts, 44, 5))
180+
>message : Symbol(message, Decl(narrowingUnionWithBang.ts, 44, 21))
181+
182+
{ name: 'Error5', message: string } |
183+
>name : Symbol(name, Decl(narrowingUnionWithBang.ts, 45, 5))
184+
>message : Symbol(message, Decl(narrowingUnionWithBang.ts, 45, 21))
185+
186+
{ name: 'Error6', message: string } |
187+
>name : Symbol(name, Decl(narrowingUnionWithBang.ts, 46, 5))
188+
>message : Symbol(message, Decl(narrowingUnionWithBang.ts, 46, 21))
189+
190+
{ name: 'Error7', message: string } |
191+
>name : Symbol(name, Decl(narrowingUnionWithBang.ts, 47, 5))
192+
>message : Symbol(message, Decl(narrowingUnionWithBang.ts, 47, 21))
193+
194+
{ name: 'Error8', message: string } |
195+
>name : Symbol(name, Decl(narrowingUnionWithBang.ts, 48, 5))
196+
>message : Symbol(message, Decl(narrowingUnionWithBang.ts, 48, 21))
197+
198+
{ name: 'Correct', id: string }
199+
>name : Symbol(name, Decl(narrowingUnionWithBang.ts, 49, 5))
200+
>id : Symbol(id, Decl(narrowingUnionWithBang.ts, 49, 22))
201+
202+
};
203+
const fixed: FixedType = null as unknown as FixedType;
204+
>fixed : Symbol(fixed, Decl(narrowingUnionWithBang.ts, 51, 5))
205+
>FixedType : Symbol(FixedType, Decl(narrowingUnionWithBang.ts, 37, 1))
206+
>FixedType : Symbol(FixedType, Decl(narrowingUnionWithBang.ts, 37, 1))
207+
208+
if (fixed.thing?.name !== "Correct") {
209+
>fixed.thing?.name : Symbol(name, Decl(narrowingUnionWithBang.ts, 41, 5), Decl(narrowingUnionWithBang.ts, 42, 5), Decl(narrowingUnionWithBang.ts, 43, 5), Decl(narrowingUnionWithBang.ts, 44, 5), Decl(narrowingUnionWithBang.ts, 45, 5) ... and 4 more)
210+
>fixed.thing : Symbol(thing, Decl(narrowingUnionWithBang.ts, 39, 25))
211+
>fixed : Symbol(fixed, Decl(narrowingUnionWithBang.ts, 51, 5))
212+
>thing : Symbol(thing, Decl(narrowingUnionWithBang.ts, 39, 25))
213+
>name : Symbol(name, Decl(narrowingUnionWithBang.ts, 41, 5), Decl(narrowingUnionWithBang.ts, 42, 5), Decl(narrowingUnionWithBang.ts, 43, 5), Decl(narrowingUnionWithBang.ts, 44, 5), Decl(narrowingUnionWithBang.ts, 45, 5) ... and 4 more)
214+
215+
console.log(fixed.thing!.message);
216+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
217+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
218+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
219+
>fixed.thing!.message : Symbol(message, Decl(narrowingUnionWithBang.ts, 41, 21), Decl(narrowingUnionWithBang.ts, 42, 21), Decl(narrowingUnionWithBang.ts, 43, 21), Decl(narrowingUnionWithBang.ts, 44, 21), Decl(narrowingUnionWithBang.ts, 45, 21) ... and 3 more)
220+
>fixed.thing : Symbol(thing, Decl(narrowingUnionWithBang.ts, 39, 25))
221+
>fixed : Symbol(fixed, Decl(narrowingUnionWithBang.ts, 51, 5))
222+
>thing : Symbol(thing, Decl(narrowingUnionWithBang.ts, 39, 25))
223+
>message : Symbol(message, Decl(narrowingUnionWithBang.ts, 41, 21), Decl(narrowingUnionWithBang.ts, 42, 21), Decl(narrowingUnionWithBang.ts, 43, 21), Decl(narrowingUnionWithBang.ts, 44, 21), Decl(narrowingUnionWithBang.ts, 45, 21) ... and 3 more)
224+
225+
} else {
226+
console.log(fixed.thing.id);
227+
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
228+
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
229+
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
230+
>fixed.thing.id : Symbol(id, Decl(narrowingUnionWithBang.ts, 49, 22))
231+
>fixed.thing : Symbol(thing, Decl(narrowingUnionWithBang.ts, 39, 25))
232+
>fixed : Symbol(fixed, Decl(narrowingUnionWithBang.ts, 51, 5))
233+
>thing : Symbol(thing, Decl(narrowingUnionWithBang.ts, 39, 25))
234+
>id : Symbol(id, Decl(narrowingUnionWithBang.ts, 49, 22))
235+
}

0 commit comments

Comments
 (0)