Skip to content

Commit 89c173f

Browse files
authored
Narrow via discriminant property through optional chain (microsoft#42450)
* Naive attempt at narrowing via discriminant property through optional chain * Clean up test
1 parent 290af69 commit 89c173f

File tree

7 files changed

+149
-8
lines changed

7 files changed

+149
-8
lines changed

src/compiler/checker.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22599,10 +22599,13 @@ namespace ts {
2259922599
if (propName === undefined) {
2260022600
return type;
2260122601
}
22602-
const propType = getTypeOfPropertyOfType(type, propName);
22602+
const includesUndefined = strictNullChecks && maybeTypeOfKind(type, TypeFlags.Undefined);
22603+
const removeOptional = includesUndefined && isOptionalChain(access);
22604+
let propType = getTypeOfPropertyOfType(removeOptional ? getTypeWithFacts(type, TypeFacts.NEUndefined) : type, propName);
2260322605
if (!propType) {
2260422606
return type;
2260522607
}
22608+
propType = removeOptional ? getOptionalType(propType) : propType;
2260622609
const narrowedPropType = narrowType(propType);
2260722610
return filterType(type, t => {
2260822611
const discriminantType = getTypeOfPropertyOrIndexSignature(t, propName);

tests/baselines/reference/controlFlowOptionalChain.symbols

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -200,14 +200,14 @@ else {
200200
>o3 : Symbol(o3, Decl(controlFlowOptionalChain.ts, 58, 13))
201201

202202
o3?.x;
203-
>o3?.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 58, 19), Decl(controlFlowOptionalChain.ts, 58, 41))
203+
>o3?.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 58, 41))
204204
>o3 : Symbol(o3, Decl(controlFlowOptionalChain.ts, 58, 13))
205-
>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 58, 19), Decl(controlFlowOptionalChain.ts, 58, 41))
205+
>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 58, 41))
206206

207207
o3.x;
208-
>o3.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 58, 19), Decl(controlFlowOptionalChain.ts, 58, 41))
208+
>o3.x : Symbol(x, Decl(controlFlowOptionalChain.ts, 58, 41))
209209
>o3 : Symbol(o3, Decl(controlFlowOptionalChain.ts, 58, 13))
210-
>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 58, 19), Decl(controlFlowOptionalChain.ts, 58, 41))
210+
>x : Symbol(x, Decl(controlFlowOptionalChain.ts, 58, 41))
211211
}
212212
o3;
213213
>o3 : Symbol(o3, Decl(controlFlowOptionalChain.ts, 58, 13))

tests/baselines/reference/controlFlowOptionalChain.types

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -223,16 +223,16 @@ if (o3?.x === 1) {
223223
}
224224
else {
225225
o3;
226-
>o3 : { x: 1; y: string; } | { x: 2; y: number; } | undefined
226+
>o3 : { x: 2; y: number; } | undefined
227227

228228
o3?.x;
229229
>o3?.x : 2 | undefined
230-
>o3 : { x: 1; y: string; } | { x: 2; y: number; } | undefined
230+
>o3 : { x: 2; y: number; } | undefined
231231
>x : 2 | undefined
232232

233233
o3.x;
234234
>o3.x : 2
235-
>o3 : { x: 1; y: string; } | { x: 2; y: number; } | undefined
235+
>o3 : { x: 2; y: number; } | undefined
236236
>x : 2
237237
}
238238
o3;
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
//// [controlFlowOptionalChain2.ts]
2+
type A = {
3+
type: 'A';
4+
name: string;
5+
}
6+
7+
type B = {
8+
type: 'B';
9+
}
10+
11+
function funcTwo(arg: A | B | undefined) {
12+
if (arg?.type === 'B') {
13+
arg; // `B`
14+
return;
15+
}
16+
17+
arg;
18+
arg?.name;
19+
}
20+
21+
22+
//// [controlFlowOptionalChain2.js]
23+
function funcTwo(arg) {
24+
if ((arg === null || arg === void 0 ? void 0 : arg.type) === 'B') {
25+
arg; // `B`
26+
return;
27+
}
28+
arg;
29+
arg === null || arg === void 0 ? void 0 : arg.name;
30+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
=== tests/cases/conformance/controlFlow/controlFlowOptionalChain2.ts ===
2+
type A = {
3+
>A : Symbol(A, Decl(controlFlowOptionalChain2.ts, 0, 0))
4+
5+
type: 'A';
6+
>type : Symbol(type, Decl(controlFlowOptionalChain2.ts, 0, 10))
7+
8+
name: string;
9+
>name : Symbol(name, Decl(controlFlowOptionalChain2.ts, 1, 12))
10+
}
11+
12+
type B = {
13+
>B : Symbol(B, Decl(controlFlowOptionalChain2.ts, 3, 1))
14+
15+
type: 'B';
16+
>type : Symbol(type, Decl(controlFlowOptionalChain2.ts, 5, 10))
17+
}
18+
19+
function funcTwo(arg: A | B | undefined) {
20+
>funcTwo : Symbol(funcTwo, Decl(controlFlowOptionalChain2.ts, 7, 1))
21+
>arg : Symbol(arg, Decl(controlFlowOptionalChain2.ts, 9, 17))
22+
>A : Symbol(A, Decl(controlFlowOptionalChain2.ts, 0, 0))
23+
>B : Symbol(B, Decl(controlFlowOptionalChain2.ts, 3, 1))
24+
25+
if (arg?.type === 'B') {
26+
>arg?.type : Symbol(type, Decl(controlFlowOptionalChain2.ts, 0, 10), Decl(controlFlowOptionalChain2.ts, 5, 10))
27+
>arg : Symbol(arg, Decl(controlFlowOptionalChain2.ts, 9, 17))
28+
>type : Symbol(type, Decl(controlFlowOptionalChain2.ts, 0, 10), Decl(controlFlowOptionalChain2.ts, 5, 10))
29+
30+
arg; // `B`
31+
>arg : Symbol(arg, Decl(controlFlowOptionalChain2.ts, 9, 17))
32+
33+
return;
34+
}
35+
36+
arg;
37+
>arg : Symbol(arg, Decl(controlFlowOptionalChain2.ts, 9, 17))
38+
39+
arg?.name;
40+
>arg?.name : Symbol(name, Decl(controlFlowOptionalChain2.ts, 1, 12))
41+
>arg : Symbol(arg, Decl(controlFlowOptionalChain2.ts, 9, 17))
42+
>name : Symbol(name, Decl(controlFlowOptionalChain2.ts, 1, 12))
43+
}
44+
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
=== tests/cases/conformance/controlFlow/controlFlowOptionalChain2.ts ===
2+
type A = {
3+
>A : A
4+
5+
type: 'A';
6+
>type : "A"
7+
8+
name: string;
9+
>name : string
10+
}
11+
12+
type B = {
13+
>B : B
14+
15+
type: 'B';
16+
>type : "B"
17+
}
18+
19+
function funcTwo(arg: A | B | undefined) {
20+
>funcTwo : (arg: A | B | undefined) => void
21+
>arg : A | B | undefined
22+
23+
if (arg?.type === 'B') {
24+
>arg?.type === 'B' : boolean
25+
>arg?.type : "A" | "B" | undefined
26+
>arg : A | B | undefined
27+
>type : "A" | "B" | undefined
28+
>'B' : "B"
29+
30+
arg; // `B`
31+
>arg : B
32+
33+
return;
34+
}
35+
36+
arg;
37+
>arg : A | undefined
38+
39+
arg?.name;
40+
>arg?.name : string | undefined
41+
>arg : A | undefined
42+
>name : string | undefined
43+
}
44+
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// @strictNullChecks: true
2+
3+
type A = {
4+
type: 'A';
5+
name: string;
6+
}
7+
8+
type B = {
9+
type: 'B';
10+
}
11+
12+
function funcTwo(arg: A | B | undefined) {
13+
if (arg?.type === 'B') {
14+
arg; // `B`
15+
return;
16+
}
17+
18+
arg;
19+
arg?.name;
20+
}

0 commit comments

Comments
 (0)