Skip to content

Commit c15f40a

Browse files
authored
Fix discriminant property narrowing through optional chain with null (microsoft#42503)
* Fix discriminant property narrowing through optional chain with null * Accept baselines * Add tests from Anders
1 parent 65e4d60 commit c15f40a

File tree

5 files changed

+660
-4
lines changed

5 files changed

+660
-4
lines changed

src/compiler/checker.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -22641,13 +22641,13 @@ namespace ts {
2264122641
if (propName === undefined) {
2264222642
return type;
2264322643
}
22644-
const includesUndefined = strictNullChecks && maybeTypeOfKind(type, TypeFlags.Undefined);
22645-
const removeOptional = includesUndefined && isOptionalChain(access);
22646-
let propType = getTypeOfPropertyOfType(removeOptional ? getTypeWithFacts(type, TypeFacts.NEUndefined) : type, propName);
22644+
const includesNullable = strictNullChecks && maybeTypeOfKind(type, TypeFlags.Nullable);
22645+
const removeNullable = includesNullable && isOptionalChain(access);
22646+
let propType = getTypeOfPropertyOfType(removeNullable ? getTypeWithFacts(type, TypeFacts.NEUndefinedOrNull) : type, propName);
2264722647
if (!propType) {
2264822648
return type;
2264922649
}
22650-
propType = removeOptional ? getOptionalType(propType) : propType;
22650+
propType = removeNullable ? getOptionalType(propType) : propType;
2265122651
const narrowedPropType = narrowType(propType);
2265222652
return filterType(type, t => {
2265322653
const discriminantType = getTypeOfPropertyOrIndexSignature(t, propName);

tests/baselines/reference/controlFlowOptionalChain2.js

+158
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,92 @@ function funcTwo(arg: A | B | undefined) {
1717
arg;
1818
arg?.name;
1919
}
20+
21+
function funcThree(arg: A | B | null) {
22+
if (arg?.type === 'B') {
23+
arg; // `B`
24+
return;
25+
}
26+
27+
arg;
28+
arg?.name;
29+
}
30+
31+
type U = { kind: undefined, u: 'u' }
32+
type N = { kind: null, n: 'n' }
33+
type X = { kind: 'X', x: 'x' }
34+
35+
function f1(x: X | U | undefined) {
36+
if (x?.kind === undefined) {
37+
x; // U | undefined
38+
}
39+
else {
40+
x; // X
41+
}
42+
}
43+
44+
function f2(x: X | N | undefined) {
45+
if (x?.kind === undefined) {
46+
x; // undefined
47+
}
48+
else {
49+
x; // X | N
50+
}
51+
}
52+
53+
function f3(x: X | U | null) {
54+
if (x?.kind === undefined) {
55+
x; // U | null
56+
}
57+
else {
58+
x; // X
59+
}
60+
}
61+
62+
function f4(x: X | N | null) {
63+
if (x?.kind === undefined) {
64+
x; // null
65+
}
66+
else {
67+
x; // X | N
68+
}
69+
}
70+
71+
function f5(x: X | U | undefined) {
72+
if (x?.kind === null) {
73+
x; // never
74+
}
75+
else {
76+
x; // X | U | undefined
77+
}
78+
}
79+
80+
function f6(x: X | N | undefined) {
81+
if (x?.kind === null) {
82+
x; // N
83+
}
84+
else {
85+
x; // X | undefined
86+
}
87+
}
88+
89+
function f7(x: X | U | null) {
90+
if (x?.kind === null) {
91+
x; // never
92+
}
93+
else {
94+
x; // X | U | null
95+
}
96+
}
97+
98+
function f8(x: X | N | null) {
99+
if (x?.kind === null) {
100+
x; // N
101+
}
102+
else {
103+
x; // X | null
104+
}
105+
}
20106

21107

22108
//// [controlFlowOptionalChain2.js]
@@ -28,3 +114,75 @@ function funcTwo(arg) {
28114
arg;
29115
arg === null || arg === void 0 ? void 0 : arg.name;
30116
}
117+
function funcThree(arg) {
118+
if ((arg === null || arg === void 0 ? void 0 : arg.type) === 'B') {
119+
arg; // `B`
120+
return;
121+
}
122+
arg;
123+
arg === null || arg === void 0 ? void 0 : arg.name;
124+
}
125+
function f1(x) {
126+
if ((x === null || x === void 0 ? void 0 : x.kind) === undefined) {
127+
x; // U | undefined
128+
}
129+
else {
130+
x; // X
131+
}
132+
}
133+
function f2(x) {
134+
if ((x === null || x === void 0 ? void 0 : x.kind) === undefined) {
135+
x; // undefined
136+
}
137+
else {
138+
x; // X | N
139+
}
140+
}
141+
function f3(x) {
142+
if ((x === null || x === void 0 ? void 0 : x.kind) === undefined) {
143+
x; // U | null
144+
}
145+
else {
146+
x; // X
147+
}
148+
}
149+
function f4(x) {
150+
if ((x === null || x === void 0 ? void 0 : x.kind) === undefined) {
151+
x; // null
152+
}
153+
else {
154+
x; // X | N
155+
}
156+
}
157+
function f5(x) {
158+
if ((x === null || x === void 0 ? void 0 : x.kind) === null) {
159+
x; // never
160+
}
161+
else {
162+
x; // X | U | undefined
163+
}
164+
}
165+
function f6(x) {
166+
if ((x === null || x === void 0 ? void 0 : x.kind) === null) {
167+
x; // N
168+
}
169+
else {
170+
x; // X | undefined
171+
}
172+
}
173+
function f7(x) {
174+
if ((x === null || x === void 0 ? void 0 : x.kind) === null) {
175+
x; // never
176+
}
177+
else {
178+
x; // X | U | null
179+
}
180+
}
181+
function f8(x) {
182+
if ((x === null || x === void 0 ? void 0 : x.kind) === null) {
183+
x; // N
184+
}
185+
else {
186+
x; // X | null
187+
}
188+
}

tests/baselines/reference/controlFlowOptionalChain2.symbols

+205
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,208 @@ function funcTwo(arg: A | B | undefined) {
4242
>name : Symbol(name, Decl(controlFlowOptionalChain2.ts, 1, 12))
4343
}
4444

45+
function funcThree(arg: A | B | null) {
46+
>funcThree : Symbol(funcThree, Decl(controlFlowOptionalChain2.ts, 17, 1))
47+
>arg : Symbol(arg, Decl(controlFlowOptionalChain2.ts, 19, 19))
48+
>A : Symbol(A, Decl(controlFlowOptionalChain2.ts, 0, 0))
49+
>B : Symbol(B, Decl(controlFlowOptionalChain2.ts, 3, 1))
50+
51+
if (arg?.type === 'B') {
52+
>arg?.type : Symbol(type, Decl(controlFlowOptionalChain2.ts, 0, 10), Decl(controlFlowOptionalChain2.ts, 5, 10))
53+
>arg : Symbol(arg, Decl(controlFlowOptionalChain2.ts, 19, 19))
54+
>type : Symbol(type, Decl(controlFlowOptionalChain2.ts, 0, 10), Decl(controlFlowOptionalChain2.ts, 5, 10))
55+
56+
arg; // `B`
57+
>arg : Symbol(arg, Decl(controlFlowOptionalChain2.ts, 19, 19))
58+
59+
return;
60+
}
61+
62+
arg;
63+
>arg : Symbol(arg, Decl(controlFlowOptionalChain2.ts, 19, 19))
64+
65+
arg?.name;
66+
>arg?.name : Symbol(name, Decl(controlFlowOptionalChain2.ts, 1, 12))
67+
>arg : Symbol(arg, Decl(controlFlowOptionalChain2.ts, 19, 19))
68+
>name : Symbol(name, Decl(controlFlowOptionalChain2.ts, 1, 12))
69+
}
70+
71+
type U = { kind: undefined, u: 'u' }
72+
>U : Symbol(U, Decl(controlFlowOptionalChain2.ts, 27, 1))
73+
>kind : Symbol(kind, Decl(controlFlowOptionalChain2.ts, 29, 10))
74+
>u : Symbol(u, Decl(controlFlowOptionalChain2.ts, 29, 27))
75+
76+
type N = { kind: null, n: 'n' }
77+
>N : Symbol(N, Decl(controlFlowOptionalChain2.ts, 29, 36))
78+
>kind : Symbol(kind, Decl(controlFlowOptionalChain2.ts, 30, 10))
79+
>n : Symbol(n, Decl(controlFlowOptionalChain2.ts, 30, 22))
80+
81+
type X = { kind: 'X', x: 'x' }
82+
>X : Symbol(X, Decl(controlFlowOptionalChain2.ts, 30, 31))
83+
>kind : Symbol(kind, Decl(controlFlowOptionalChain2.ts, 31, 10))
84+
>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 31, 21))
85+
86+
function f1(x: X | U | undefined) {
87+
>f1 : Symbol(f1, Decl(controlFlowOptionalChain2.ts, 31, 30))
88+
>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 33, 12))
89+
>X : Symbol(X, Decl(controlFlowOptionalChain2.ts, 30, 31))
90+
>U : Symbol(U, Decl(controlFlowOptionalChain2.ts, 27, 1))
91+
92+
if (x?.kind === undefined) {
93+
>x?.kind : Symbol(kind, Decl(controlFlowOptionalChain2.ts, 29, 10), Decl(controlFlowOptionalChain2.ts, 31, 10))
94+
>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 33, 12))
95+
>kind : Symbol(kind, Decl(controlFlowOptionalChain2.ts, 29, 10), Decl(controlFlowOptionalChain2.ts, 31, 10))
96+
>undefined : Symbol(undefined)
97+
98+
x; // U | undefined
99+
>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 33, 12))
100+
}
101+
else {
102+
x; // X
103+
>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 33, 12))
104+
}
105+
}
106+
107+
function f2(x: X | N | undefined) {
108+
>f2 : Symbol(f2, Decl(controlFlowOptionalChain2.ts, 40, 1))
109+
>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 42, 12))
110+
>X : Symbol(X, Decl(controlFlowOptionalChain2.ts, 30, 31))
111+
>N : Symbol(N, Decl(controlFlowOptionalChain2.ts, 29, 36))
112+
113+
if (x?.kind === undefined) {
114+
>x?.kind : Symbol(kind, Decl(controlFlowOptionalChain2.ts, 30, 10), Decl(controlFlowOptionalChain2.ts, 31, 10))
115+
>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 42, 12))
116+
>kind : Symbol(kind, Decl(controlFlowOptionalChain2.ts, 30, 10), Decl(controlFlowOptionalChain2.ts, 31, 10))
117+
>undefined : Symbol(undefined)
118+
119+
x; // undefined
120+
>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 42, 12))
121+
}
122+
else {
123+
x; // X | N
124+
>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 42, 12))
125+
}
126+
}
127+
128+
function f3(x: X | U | null) {
129+
>f3 : Symbol(f3, Decl(controlFlowOptionalChain2.ts, 49, 1))
130+
>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 51, 12))
131+
>X : Symbol(X, Decl(controlFlowOptionalChain2.ts, 30, 31))
132+
>U : Symbol(U, Decl(controlFlowOptionalChain2.ts, 27, 1))
133+
134+
if (x?.kind === undefined) {
135+
>x?.kind : Symbol(kind, Decl(controlFlowOptionalChain2.ts, 29, 10), Decl(controlFlowOptionalChain2.ts, 31, 10))
136+
>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 51, 12))
137+
>kind : Symbol(kind, Decl(controlFlowOptionalChain2.ts, 29, 10), Decl(controlFlowOptionalChain2.ts, 31, 10))
138+
>undefined : Symbol(undefined)
139+
140+
x; // U | null
141+
>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 51, 12))
142+
}
143+
else {
144+
x; // X
145+
>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 51, 12))
146+
}
147+
}
148+
149+
function f4(x: X | N | null) {
150+
>f4 : Symbol(f4, Decl(controlFlowOptionalChain2.ts, 58, 1))
151+
>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 60, 12))
152+
>X : Symbol(X, Decl(controlFlowOptionalChain2.ts, 30, 31))
153+
>N : Symbol(N, Decl(controlFlowOptionalChain2.ts, 29, 36))
154+
155+
if (x?.kind === undefined) {
156+
>x?.kind : Symbol(kind, Decl(controlFlowOptionalChain2.ts, 30, 10), Decl(controlFlowOptionalChain2.ts, 31, 10))
157+
>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 60, 12))
158+
>kind : Symbol(kind, Decl(controlFlowOptionalChain2.ts, 30, 10), Decl(controlFlowOptionalChain2.ts, 31, 10))
159+
>undefined : Symbol(undefined)
160+
161+
x; // null
162+
>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 60, 12))
163+
}
164+
else {
165+
x; // X | N
166+
>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 60, 12))
167+
}
168+
}
169+
170+
function f5(x: X | U | undefined) {
171+
>f5 : Symbol(f5, Decl(controlFlowOptionalChain2.ts, 67, 1))
172+
>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 69, 12))
173+
>X : Symbol(X, Decl(controlFlowOptionalChain2.ts, 30, 31))
174+
>U : Symbol(U, Decl(controlFlowOptionalChain2.ts, 27, 1))
175+
176+
if (x?.kind === null) {
177+
>x?.kind : Symbol(kind, Decl(controlFlowOptionalChain2.ts, 29, 10), Decl(controlFlowOptionalChain2.ts, 31, 10))
178+
>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 69, 12))
179+
>kind : Symbol(kind, Decl(controlFlowOptionalChain2.ts, 29, 10), Decl(controlFlowOptionalChain2.ts, 31, 10))
180+
181+
x; // never
182+
>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 69, 12))
183+
}
184+
else {
185+
x; // X | U | undefined
186+
>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 69, 12))
187+
}
188+
}
189+
190+
function f6(x: X | N | undefined) {
191+
>f6 : Symbol(f6, Decl(controlFlowOptionalChain2.ts, 76, 1))
192+
>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 78, 12))
193+
>X : Symbol(X, Decl(controlFlowOptionalChain2.ts, 30, 31))
194+
>N : Symbol(N, Decl(controlFlowOptionalChain2.ts, 29, 36))
195+
196+
if (x?.kind === null) {
197+
>x?.kind : Symbol(kind, Decl(controlFlowOptionalChain2.ts, 30, 10), Decl(controlFlowOptionalChain2.ts, 31, 10))
198+
>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 78, 12))
199+
>kind : Symbol(kind, Decl(controlFlowOptionalChain2.ts, 30, 10), Decl(controlFlowOptionalChain2.ts, 31, 10))
200+
201+
x; // N
202+
>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 78, 12))
203+
}
204+
else {
205+
x; // X | undefined
206+
>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 78, 12))
207+
}
208+
}
209+
210+
function f7(x: X | U | null) {
211+
>f7 : Symbol(f7, Decl(controlFlowOptionalChain2.ts, 85, 1))
212+
>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 87, 12))
213+
>X : Symbol(X, Decl(controlFlowOptionalChain2.ts, 30, 31))
214+
>U : Symbol(U, Decl(controlFlowOptionalChain2.ts, 27, 1))
215+
216+
if (x?.kind === null) {
217+
>x?.kind : Symbol(kind, Decl(controlFlowOptionalChain2.ts, 29, 10), Decl(controlFlowOptionalChain2.ts, 31, 10))
218+
>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 87, 12))
219+
>kind : Symbol(kind, Decl(controlFlowOptionalChain2.ts, 29, 10), Decl(controlFlowOptionalChain2.ts, 31, 10))
220+
221+
x; // never
222+
>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 87, 12))
223+
}
224+
else {
225+
x; // X | U | null
226+
>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 87, 12))
227+
}
228+
}
229+
230+
function f8(x: X | N | null) {
231+
>f8 : Symbol(f8, Decl(controlFlowOptionalChain2.ts, 94, 1))
232+
>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 96, 12))
233+
>X : Symbol(X, Decl(controlFlowOptionalChain2.ts, 30, 31))
234+
>N : Symbol(N, Decl(controlFlowOptionalChain2.ts, 29, 36))
235+
236+
if (x?.kind === null) {
237+
>x?.kind : Symbol(kind, Decl(controlFlowOptionalChain2.ts, 30, 10), Decl(controlFlowOptionalChain2.ts, 31, 10))
238+
>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 96, 12))
239+
>kind : Symbol(kind, Decl(controlFlowOptionalChain2.ts, 30, 10), Decl(controlFlowOptionalChain2.ts, 31, 10))
240+
241+
x; // N
242+
>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 96, 12))
243+
}
244+
else {
245+
x; // X | null
246+
>x : Symbol(x, Decl(controlFlowOptionalChain2.ts, 96, 12))
247+
}
248+
}
249+

0 commit comments

Comments
 (0)