Skip to content

Commit 40521d7

Browse files
committed
Allow type narrowed by multi property access for discriminant/typeof/truthiness
add tests update tests fix remove useless code fix test reference
1 parent a75f26e commit 40521d7

File tree

56 files changed

+8357
-247
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+8357
-247
lines changed

src/compiler/checker.ts

Lines changed: 403 additions & 80 deletions
Large diffs are not rendered by default.

tests/baselines/reference/controlFlowOptionalChain.errors.txt

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,11 +57,10 @@ tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(463,9): error TS
5757
tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(498,13): error TS2532: Object is possibly 'undefined'.
5858
tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(501,13): error TS2532: Object is possibly 'undefined'.
5959
tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(515,13): error TS2532: Object is possibly 'undefined'.
60-
tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(518,13): error TS2532: Object is possibly 'undefined'.
6160
tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(567,21): error TS2532: Object is possibly 'undefined'.
6261

6362

64-
==== tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts (61 errors) ====
63+
==== tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts (60 errors) ====
6564
// assignments in shortcutting chain
6665
declare const o: undefined | {
6766
[key: string]: any;
@@ -697,9 +696,7 @@ tests/cases/conformance/controlFlow/controlFlowOptionalChain.ts(567,21): error T
697696
!!! error TS2532: Object is possibly 'undefined'.
698697
break;
699698
default:
700-
o.foo; // Error
701-
~
702-
!!! error TS2532: Object is possibly 'undefined'.
699+
o.foo;
703700
break;
704701
}
705702
}

tests/baselines/reference/controlFlowOptionalChain.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -516,7 +516,7 @@ function f41(o: Thing | undefined) {
516516
o.foo; // Error
517517
break;
518518
default:
519-
o.foo; // Error
519+
o.foo;
520520
break;
521521
}
522522
}
@@ -1049,7 +1049,7 @@ function f41(o) {
10491049
o.foo; // Error
10501050
break;
10511051
default:
1052-
o.foo; // Error
1052+
o.foo;
10531053
break;
10541054
}
10551055
}

tests/baselines/reference/controlFlowOptionalChain.symbols

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1438,9 +1438,7 @@ function f23(o: Thing | undefined) {
14381438
>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14))
14391439

14401440
o.foo; // Error
1441-
>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14))
14421441
>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 439, 13))
1443-
>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14))
14441442
}
14451443
else {
14461444
o.foo;
@@ -1460,19 +1458,15 @@ function f23(o: Thing | undefined) {
14601458
}
14611459
else {
14621460
o.foo; // Error
1463-
>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14))
14641461
>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 439, 13))
1465-
>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14))
14661462
}
14671463
if (typeof o?.foo == "undefined") {
14681464
>o?.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14))
14691465
>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 439, 13))
14701466
>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14))
14711467

14721468
o.foo; // Error
1473-
>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14))
14741469
>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 439, 13))
1475-
>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14))
14761470
}
14771471
else {
14781472
o.foo;
@@ -1492,9 +1486,7 @@ function f23(o: Thing | undefined) {
14921486
}
14931487
else {
14941488
o.foo; // Error
1495-
>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14))
14961489
>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 439, 13))
1497-
>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14))
14981490
}
14991491
}
15001492

@@ -1636,13 +1628,11 @@ function f41(o: Thing | undefined) {
16361628
break;
16371629
case "undefined":
16381630
o.foo; // Error
1639-
>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14))
16401631
>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 505, 13))
1641-
>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14))
16421632

16431633
break;
16441634
default:
1645-
o.foo; // Error
1635+
o.foo;
16461636
>o.foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14))
16471637
>o : Symbol(o, Decl(controlFlowOptionalChain.ts, 505, 13))
16481638
>foo : Symbol(foo, Decl(controlFlowOptionalChain.ts, 161, 14))

tests/baselines/reference/controlFlowOptionalChain.types

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1618,9 +1618,9 @@ function f23(o: Thing | undefined) {
16181618
>"undefined" : "undefined"
16191619

16201620
o.foo; // Error
1621-
>o.foo : never
1622-
>o : Thing | undefined
1623-
>foo : never
1621+
>o.foo : any
1622+
>o : undefined
1623+
>foo : any
16241624
}
16251625
else {
16261626
o.foo;
@@ -1643,9 +1643,9 @@ function f23(o: Thing | undefined) {
16431643
}
16441644
else {
16451645
o.foo; // Error
1646-
>o.foo : never
1647-
>o : Thing | undefined
1648-
>foo : never
1646+
>o.foo : any
1647+
>o : undefined
1648+
>foo : any
16491649
}
16501650
if (typeof o?.foo == "undefined") {
16511651
>typeof o?.foo == "undefined" : boolean
@@ -1656,9 +1656,9 @@ function f23(o: Thing | undefined) {
16561656
>"undefined" : "undefined"
16571657

16581658
o.foo; // Error
1659-
>o.foo : never
1660-
>o : Thing | undefined
1661-
>foo : never
1659+
>o.foo : any
1660+
>o : undefined
1661+
>foo : any
16621662
}
16631663
else {
16641664
o.foo;
@@ -1681,9 +1681,9 @@ function f23(o: Thing | undefined) {
16811681
}
16821682
else {
16831683
o.foo; // Error
1684-
>o.foo : never
1685-
>o : Thing | undefined
1686-
>foo : never
1684+
>o.foo : any
1685+
>o : undefined
1686+
>foo : any
16871687
}
16881688
}
16891689

@@ -1852,15 +1852,15 @@ function f41(o: Thing | undefined) {
18521852
>"undefined" : "undefined"
18531853

18541854
o.foo; // Error
1855-
>o.foo : never
1856-
>o : Thing | undefined
1857-
>foo : never
1855+
>o.foo : any
1856+
>o : undefined
1857+
>foo : any
18581858

18591859
break;
18601860
default:
1861-
o.foo; // Error
1861+
o.foo;
18621862
>o.foo : never
1863-
>o : Thing | undefined
1863+
>o : Thing
18641864
>foo : never
18651865

18661866
break;

tests/baselines/reference/destructuringControlFlow.types

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,34 +63,34 @@ function f2(obj: [number, string] | null[]) {
6363
>obj[0] : number | null
6464
>obj : [number, string] | null[]
6565
>0 : 0
66-
>obj[1] : string | null
67-
>obj : [number, string] | null[]
66+
>obj[1] : string
67+
>obj : [number, string]
6868
>1 : 1
6969

7070
let c0 = obj[0]; // number
7171
>c0 : number
7272
>obj[0] : number
73-
>obj : [number, string] | null[]
73+
>obj : [number, string]
7474
>0 : 0
7575

7676
let c1 = obj[1]; // string
7777
>c1 : string
7878
>obj[1] : string
79-
>obj : [number, string] | null[]
79+
>obj : [number, string]
8080
>1 : 1
8181

8282
let [d0, d1] = obj;
8383
>d0 : number
8484
>d1 : string
85-
>obj : [number, string] | null[]
85+
>obj : [number, string]
8686

8787
([c0, c1] = obj);
88-
>([c0, c1] = obj) : [number, string] | null[]
89-
>[c0, c1] = obj : [number, string] | null[]
88+
>([c0, c1] = obj) : [number, string]
89+
>[c0, c1] = obj : [number, string]
9090
>[c0, c1] : [number, string]
9191
>c0 : number
9292
>c1 : string
93-
>obj : [number, string] | null[]
93+
>obj : [number, string]
9494
}
9595
}
9696

tests/baselines/reference/discriminantsAndTypePredicates.types

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,5 +86,5 @@ function foo2(x: A | B): any {
8686
>x : B
8787
}
8888
x; // never
89-
>x : never
89+
>x : B
9090
}

tests/baselines/reference/discriminatedUnionTypes2.errors.txt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ tests/cases/conformance/types/union/discriminatedUnionTypes2.ts(27,30): error TS
22
Object literal may only specify known properties, and 'c' does not exist in type '{ a: null; b: string; }'.
33
tests/cases/conformance/types/union/discriminatedUnionTypes2.ts(32,11): error TS2339: Property 'b' does not exist on type '{ a: 0; b: string; } | { a: T; c: number; }'.
44
Property 'b' does not exist on type '{ a: T; c: number; }'.
5+
tests/cases/conformance/types/union/discriminatedUnionTypes2.ts(132,11): error TS2339: Property 'value' does not exist on type 'never'.
56

67

7-
==== tests/cases/conformance/types/union/discriminatedUnionTypes2.ts (2 errors) ====
8+
==== tests/cases/conformance/types/union/discriminatedUnionTypes2.ts (3 errors) ====
89
function f10(x : { kind: false, a: string } | { kind: true, b: string } | { kind: string, c: string }) {
910
if (x.kind === false) {
1011
x.a;
@@ -142,7 +143,9 @@ tests/cases/conformance/types/union/discriminatedUnionTypes2.ts(32,11): error TS
142143
x.value; // number
143144
}
144145
else {
145-
x.value; // number
146+
x.value; // Error, x is never
147+
~~~~~
148+
!!! error TS2339: Property 'value' does not exist on type 'never'.
146149
}
147150
}
148151

tests/baselines/reference/discriminatedUnionTypes2.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ function foo1(x: RuntimeValue & { type: 'number' }) {
130130
x.value; // number
131131
}
132132
else {
133-
x.value; // number
133+
x.value; // Error, x is never
134134
}
135135
}
136136

@@ -226,7 +226,7 @@ function foo1(x) {
226226
x.value; // number
227227
}
228228
else {
229-
x.value; // number
229+
x.value; // Error, x is never
230230
}
231231
}
232232
function foo2(x) {

tests/baselines/reference/discriminatedUnionTypes2.symbols

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -366,10 +366,8 @@ function foo1(x: RuntimeValue & { type: 'number' }) {
366366
>value : Symbol(value, Decl(discriminatedUnionTypes2.ts, 122, 23))
367367
}
368368
else {
369-
x.value; // number
370-
>x.value : Symbol(value, Decl(discriminatedUnionTypes2.ts, 122, 23))
369+
x.value; // Error, x is never
371370
>x : Symbol(x, Decl(discriminatedUnionTypes2.ts, 126, 14))
372-
>value : Symbol(value, Decl(discriminatedUnionTypes2.ts, 122, 23))
373371
}
374372
}
375373

tests/baselines/reference/discriminatedUnionTypes2.types

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -379,10 +379,10 @@ function foo1(x: RuntimeValue & { type: 'number' }) {
379379
>value : number
380380
}
381381
else {
382-
x.value; // number
383-
>x.value : number
384-
>x : { type: "number"; value: number; } & { type: "number"; }
385-
>value : number
382+
x.value; // Error, x is never
383+
>x.value : any
384+
>x : never
385+
>value : any
386386
}
387387
}
388388

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
tests/cases/conformance/types/union/discriminatedUnionTypes3.ts(59,11): error TS2339: Property 's' does not exist on type 'T'.
2+
tests/cases/conformance/types/union/discriminatedUnionTypes3.ts(78,10): error TS2339: Property 'property' does not exist on type 'SomeReturnType'.
3+
Property 'property' does not exist on type 'Err'.
4+
5+
6+
==== tests/cases/conformance/types/union/discriminatedUnionTypes3.ts (2 errors) ====
7+
type A = { type2: "a", a: number }
8+
type B = { type2: "b", b: number }
9+
type C = { type2: "c", b: number | string }
10+
11+
type X = { type1: A, x: string }
12+
type Y = { type1: B, y: string }
13+
type Z = { type1: C, z: string }
14+
type W = { type1: undefined }
15+
16+
function f32(x: X | Y) {
17+
switch (x.type1.type2) {
18+
case "a":
19+
x.x // typeof x is X
20+
break;
21+
case "b":
22+
x.y // typeof x is Y
23+
break;
24+
}
25+
}
26+
27+
function f33(x: A | B) {
28+
switch (x.type2) {
29+
case "a":
30+
x // typeof x is X
31+
break;
32+
case "b":
33+
x // typeof x is Y
34+
break;
35+
}
36+
}
37+
38+
function f34(x: X | Y) {
39+
if (x.type1.type2 === "a") {
40+
x.x // typeof x is X
41+
} else if (x.type1.type2 === "b") {
42+
x.y // typeof x is Y
43+
}
44+
}
45+
46+
function f35(x: X | W) {
47+
if (x.type1?.type2 === "a") {
48+
x.x
49+
}
50+
}
51+
52+
function f36(x: X | W) {
53+
x.type1?.type2 ?? x;
54+
}
55+
56+
type S = { sub: { type0: X }, s: string }
57+
type T = { sub: { type0: Y }, t: string }
58+
59+
function f37(s: S | T) {
60+
if (s.sub.type0.type1.type2 === "a") {
61+
s.s // typeof s is S
62+
s.sub.type0.x // type of s.sub.type is X
63+
s.sub.type0.type1.a // type of s.sub.type.type is A
64+
} else {
65+
s.s // type error!
66+
~
67+
!!! error TS2339: Property 's' does not exist on type 'T'.
68+
}
69+
}
70+
71+
// Repro from #44435
72+
73+
type Correct = {
74+
code: string
75+
property: true
76+
err: undefined
77+
}
78+
type Err = {
79+
err: `${string} is wrong!`
80+
}
81+
type SomeReturnType = Correct | Err;
82+
83+
const example: SomeReturnType = {} as SomeReturnType;
84+
85+
if (example.err === undefined) {
86+
example.property; // true
87+
~~~~~~~~
88+
!!! error TS2339: Property 'property' does not exist on type 'SomeReturnType'.
89+
!!! error TS2339: Property 'property' does not exist on type 'Err'.
90+
}
91+

0 commit comments

Comments
 (0)