Skip to content

Commit d2be890

Browse files
committed
Add type narrow
1 parent 67a4943 commit d2be890

15 files changed

+688
-96
lines changed

src/compiler/binder.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -905,6 +905,9 @@ namespace ts {
905905
function isNarrowingBinaryExpression(expr: BinaryExpression) {
906906
switch (expr.operatorToken.kind) {
907907
case SyntaxKind.EqualsToken:
908+
case SyntaxKind.BarBarEqualsToken:
909+
case SyntaxKind.AmpersandAmpersandEqualsToken:
910+
case SyntaxKind.QuestionQuestionEqualsToken:
908911
return containsNarrowableReference(expr.left);
909912
case SyntaxKind.EqualsEqualsToken:
910913
case SyntaxKind.ExclamationEqualsToken:

src/compiler/checker.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20701,7 +20701,11 @@ namespace ts {
2070120701
function narrowTypeByBinaryExpression(type: Type, expr: BinaryExpression, assumeTrue: boolean): Type {
2070220702
switch (expr.operatorToken.kind) {
2070320703
case SyntaxKind.EqualsToken:
20704+
case SyntaxKind.BarBarEqualsToken:
20705+
case SyntaxKind.AmpersandAmpersandEqualsToken:
20706+
case SyntaxKind.QuestionQuestionEqualsToken:
2070420707
return narrowTypeByTruthiness(narrowType(type, expr.right, assumeTrue), expr.left, assumeTrue);
20708+
2070520709
case SyntaxKind.EqualsEqualsToken:
2070620710
case SyntaxKind.ExclamationEqualsToken:
2070720711
case SyntaxKind.EqualsEqualsEqualsToken:

tests/baselines/reference/logicalAssignment4(target=es2015).errors.txt

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
tests/cases/conformance/esnext/logicalAssignment/logicalAssignment4.ts(25,21): error TS2532: Object is possibly 'undefined'.
1+
tests/cases/conformance/esnext/logicalAssignment/logicalAssignment4.ts(39,13): error TS2532: Object is possibly 'undefined'.
2+
tests/cases/conformance/esnext/logicalAssignment/logicalAssignment4.ts(45,13): error TS2532: Object is possibly 'undefined'.
23

34

4-
==== tests/cases/conformance/esnext/logicalAssignment/logicalAssignment4.ts (1 errors) ====
5+
==== tests/cases/conformance/esnext/logicalAssignment/logicalAssignment4.ts (2 errors) ====
56
function foo1(results: number[] | undefined) {
67
(results ||= []).push(100);
78
}
@@ -24,10 +25,33 @@ tests/cases/conformance/esnext/logicalAssignment/logicalAssignment4.ts(25,21): e
2425
name: string;
2526
original?: ThingWithOriginal
2627
}
27-
function doSomethingWithAlias(thing?: ThingWithOriginal | undefined) {
28-
if (thing &&= thing.original) {
29-
console.log(thing.name);
30-
~~~~~
28+
declare const v: number
29+
function doSomethingWithAlias(thing: ThingWithOriginal | undefined, defaultValue: ThingWithOriginal | undefined) {
30+
if (v === 1) {
31+
if (thing &&= thing.original) {
32+
thing.name;
33+
}
34+
}
35+
else if (v === 2) {
36+
if (thing &&= defaultValue) {
37+
thing.name;
38+
defaultValue.name
39+
}
40+
}
41+
else if (v === 3) {
42+
if (thing ||= defaultValue) {
43+
thing.name;
44+
defaultValue.name;
45+
~~~~~~~~~~~~
46+
!!! error TS2532: Object is possibly 'undefined'.
47+
}
48+
}
49+
else {
50+
if (thing ??= defaultValue) {
51+
thing.name;
52+
defaultValue.name;
53+
~~~~~~~~~~~~
3154
!!! error TS2532: Object is possibly 'undefined'.
55+
}
3256
}
3357
}

tests/baselines/reference/logicalAssignment4(target=es2015).js

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,30 @@ interface ThingWithOriginal {
2121
name: string;
2222
original?: ThingWithOriginal
2323
}
24-
function doSomethingWithAlias(thing?: ThingWithOriginal | undefined) {
25-
if (thing &&= thing.original) {
26-
console.log(thing.name);
24+
declare const v: number
25+
function doSomethingWithAlias(thing: ThingWithOriginal | undefined, defaultValue: ThingWithOriginal | undefined) {
26+
if (v === 1) {
27+
if (thing &&= thing.original) {
28+
thing.name;
29+
}
30+
}
31+
else if (v === 2) {
32+
if (thing &&= defaultValue) {
33+
thing.name;
34+
defaultValue.name
35+
}
36+
}
37+
else if (v === 3) {
38+
if (thing ||= defaultValue) {
39+
thing.name;
40+
defaultValue.name;
41+
}
42+
}
43+
else {
44+
if (thing ??= defaultValue) {
45+
thing.name;
46+
defaultValue.name;
47+
}
2748
}
2849
}
2950

@@ -43,8 +64,28 @@ function foo4(results) {
4364
results !== null && results !== void 0 ? results : (results = []);
4465
results.push(100);
4566
}
46-
function doSomethingWithAlias(thing) {
47-
if (thing && (thing = thing.original)) {
48-
console.log(thing.name);
67+
function doSomethingWithAlias(thing, defaultValue) {
68+
if (v === 1) {
69+
if (thing && (thing = thing.original)) {
70+
thing.name;
71+
}
72+
}
73+
else if (v === 2) {
74+
if (thing && (thing = defaultValue)) {
75+
thing.name;
76+
defaultValue.name;
77+
}
78+
}
79+
else if (v === 3) {
80+
if (thing || (thing = defaultValue)) {
81+
thing.name;
82+
defaultValue.name;
83+
}
84+
}
85+
else {
86+
if (thing !== null && thing !== void 0 ? thing : (thing = defaultValue)) {
87+
thing.name;
88+
defaultValue.name;
89+
}
4990
}
5091
}

tests/baselines/reference/logicalAssignment4(target=es2015).symbols

Lines changed: 69 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -55,23 +55,81 @@ interface ThingWithOriginal {
5555
>original : Symbol(ThingWithOriginal.original, Decl(logicalAssignment4.ts, 19, 17))
5656
>ThingWithOriginal : Symbol(ThingWithOriginal, Decl(logicalAssignment4.ts, 16, 1))
5757
}
58-
function doSomethingWithAlias(thing?: ThingWithOriginal | undefined) {
59-
>doSomethingWithAlias : Symbol(doSomethingWithAlias, Decl(logicalAssignment4.ts, 21, 1))
60-
>thing : Symbol(thing, Decl(logicalAssignment4.ts, 22, 30))
58+
declare const v: number
59+
>v : Symbol(v, Decl(logicalAssignment4.ts, 22, 13))
60+
61+
function doSomethingWithAlias(thing: ThingWithOriginal | undefined, defaultValue: ThingWithOriginal | undefined) {
62+
>doSomethingWithAlias : Symbol(doSomethingWithAlias, Decl(logicalAssignment4.ts, 22, 23))
63+
>thing : Symbol(thing, Decl(logicalAssignment4.ts, 23, 30))
64+
>ThingWithOriginal : Symbol(ThingWithOriginal, Decl(logicalAssignment4.ts, 16, 1))
65+
>defaultValue : Symbol(defaultValue, Decl(logicalAssignment4.ts, 23, 67))
6166
>ThingWithOriginal : Symbol(ThingWithOriginal, Decl(logicalAssignment4.ts, 16, 1))
6267

63-
if (thing &&= thing.original) {
64-
>thing : Symbol(thing, Decl(logicalAssignment4.ts, 22, 30))
68+
if (v === 1) {
69+
>v : Symbol(v, Decl(logicalAssignment4.ts, 22, 13))
70+
71+
if (thing &&= thing.original) {
72+
>thing : Symbol(thing, Decl(logicalAssignment4.ts, 23, 30))
6573
>thing.original : Symbol(ThingWithOriginal.original, Decl(logicalAssignment4.ts, 19, 17))
66-
>thing : Symbol(thing, Decl(logicalAssignment4.ts, 22, 30))
74+
>thing : Symbol(thing, Decl(logicalAssignment4.ts, 23, 30))
6775
>original : Symbol(ThingWithOriginal.original, Decl(logicalAssignment4.ts, 19, 17))
6876

69-
console.log(thing.name);
70-
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
71-
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
72-
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
77+
thing.name;
78+
>thing.name : Symbol(ThingWithOriginal.name, Decl(logicalAssignment4.ts, 18, 29))
79+
>thing : Symbol(thing, Decl(logicalAssignment4.ts, 23, 30))
80+
>name : Symbol(ThingWithOriginal.name, Decl(logicalAssignment4.ts, 18, 29))
81+
}
82+
}
83+
else if (v === 2) {
84+
>v : Symbol(v, Decl(logicalAssignment4.ts, 22, 13))
85+
86+
if (thing &&= defaultValue) {
87+
>thing : Symbol(thing, Decl(logicalAssignment4.ts, 23, 30))
88+
>defaultValue : Symbol(defaultValue, Decl(logicalAssignment4.ts, 23, 67))
89+
90+
thing.name;
7391
>thing.name : Symbol(ThingWithOriginal.name, Decl(logicalAssignment4.ts, 18, 29))
74-
>thing : Symbol(thing, Decl(logicalAssignment4.ts, 22, 30))
92+
>thing : Symbol(thing, Decl(logicalAssignment4.ts, 23, 30))
93+
>name : Symbol(ThingWithOriginal.name, Decl(logicalAssignment4.ts, 18, 29))
94+
95+
defaultValue.name
96+
>defaultValue.name : Symbol(ThingWithOriginal.name, Decl(logicalAssignment4.ts, 18, 29))
97+
>defaultValue : Symbol(defaultValue, Decl(logicalAssignment4.ts, 23, 67))
98+
>name : Symbol(ThingWithOriginal.name, Decl(logicalAssignment4.ts, 18, 29))
99+
}
100+
}
101+
else if (v === 3) {
102+
>v : Symbol(v, Decl(logicalAssignment4.ts, 22, 13))
103+
104+
if (thing ||= defaultValue) {
105+
>thing : Symbol(thing, Decl(logicalAssignment4.ts, 23, 30))
106+
>defaultValue : Symbol(defaultValue, Decl(logicalAssignment4.ts, 23, 67))
107+
108+
thing.name;
109+
>thing.name : Symbol(ThingWithOriginal.name, Decl(logicalAssignment4.ts, 18, 29))
110+
>thing : Symbol(thing, Decl(logicalAssignment4.ts, 23, 30))
111+
>name : Symbol(ThingWithOriginal.name, Decl(logicalAssignment4.ts, 18, 29))
112+
113+
defaultValue.name;
114+
>defaultValue.name : Symbol(ThingWithOriginal.name, Decl(logicalAssignment4.ts, 18, 29))
115+
>defaultValue : Symbol(defaultValue, Decl(logicalAssignment4.ts, 23, 67))
116+
>name : Symbol(ThingWithOriginal.name, Decl(logicalAssignment4.ts, 18, 29))
117+
}
118+
}
119+
else {
120+
if (thing ??= defaultValue) {
121+
>thing : Symbol(thing, Decl(logicalAssignment4.ts, 23, 30))
122+
>defaultValue : Symbol(defaultValue, Decl(logicalAssignment4.ts, 23, 67))
123+
124+
thing.name;
125+
>thing.name : Symbol(ThingWithOriginal.name, Decl(logicalAssignment4.ts, 18, 29))
126+
>thing : Symbol(thing, Decl(logicalAssignment4.ts, 23, 30))
127+
>name : Symbol(ThingWithOriginal.name, Decl(logicalAssignment4.ts, 18, 29))
128+
129+
defaultValue.name;
130+
>defaultValue.name : Symbol(ThingWithOriginal.name, Decl(logicalAssignment4.ts, 18, 29))
131+
>defaultValue : Symbol(defaultValue, Decl(logicalAssignment4.ts, 23, 67))
75132
>name : Symbol(ThingWithOriginal.name, Decl(logicalAssignment4.ts, 18, 29))
133+
}
76134
}
77135
}

tests/baselines/reference/logicalAssignment4(target=es2015).types

Lines changed: 73 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -70,24 +70,89 @@ interface ThingWithOriginal {
7070
original?: ThingWithOriginal
7171
>original : ThingWithOriginal | undefined
7272
}
73-
function doSomethingWithAlias(thing?: ThingWithOriginal | undefined) {
74-
>doSomethingWithAlias : (thing?: ThingWithOriginal | undefined) => void
73+
declare const v: number
74+
>v : number
75+
76+
function doSomethingWithAlias(thing: ThingWithOriginal | undefined, defaultValue: ThingWithOriginal | undefined) {
77+
>doSomethingWithAlias : (thing: ThingWithOriginal | undefined, defaultValue: ThingWithOriginal | undefined) => void
7578
>thing : ThingWithOriginal | undefined
79+
>defaultValue : ThingWithOriginal | undefined
80+
81+
if (v === 1) {
82+
>v === 1 : boolean
83+
>v : number
84+
>1 : 1
7685

77-
if (thing &&= thing.original) {
86+
if (thing &&= thing.original) {
7887
>thing &&= thing.original : ThingWithOriginal | undefined
7988
>thing : ThingWithOriginal | undefined
8089
>thing.original : ThingWithOriginal | undefined
8190
>thing : ThingWithOriginal
8291
>original : ThingWithOriginal | undefined
8392

84-
console.log(thing.name);
85-
>console.log(thing.name) : void
86-
>console.log : (...data: any[]) => void
87-
>console : Console
88-
>log : (...data: any[]) => void
93+
thing.name;
94+
>thing.name : string
95+
>thing : ThingWithOriginal
96+
>name : string
97+
}
98+
}
99+
else if (v === 2) {
100+
>v === 2 : boolean
101+
>v : number
102+
>2 : 2
103+
104+
if (thing &&= defaultValue) {
105+
>thing &&= defaultValue : ThingWithOriginal | undefined
106+
>thing : ThingWithOriginal | undefined
107+
>defaultValue : ThingWithOriginal | undefined
108+
109+
thing.name;
110+
>thing.name : string
111+
>thing : ThingWithOriginal
112+
>name : string
113+
114+
defaultValue.name
115+
>defaultValue.name : string
116+
>defaultValue : ThingWithOriginal
117+
>name : string
118+
}
119+
}
120+
else if (v === 3) {
121+
>v === 3 : boolean
122+
>v : number
123+
>3 : 3
124+
125+
if (thing ||= defaultValue) {
126+
>thing ||= defaultValue : ThingWithOriginal | undefined
127+
>thing : ThingWithOriginal | undefined
128+
>defaultValue : ThingWithOriginal | undefined
129+
130+
thing.name;
89131
>thing.name : string
132+
>thing : ThingWithOriginal
133+
>name : string
134+
135+
defaultValue.name;
136+
>defaultValue.name : string
137+
>defaultValue : ThingWithOriginal | undefined
138+
>name : string
139+
}
140+
}
141+
else {
142+
if (thing ??= defaultValue) {
143+
>thing ??= defaultValue : ThingWithOriginal | undefined
90144
>thing : ThingWithOriginal | undefined
145+
>defaultValue : ThingWithOriginal | undefined
146+
147+
thing.name;
148+
>thing.name : string
149+
>thing : ThingWithOriginal
150+
>name : string
151+
152+
defaultValue.name;
153+
>defaultValue.name : string
154+
>defaultValue : ThingWithOriginal | undefined
91155
>name : string
156+
}
92157
}
93158
}

tests/baselines/reference/logicalAssignment4(target=es2020).errors.txt

Lines changed: 30 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
tests/cases/conformance/esnext/logicalAssignment/logicalAssignment4.ts(25,21): error TS2532: Object is possibly 'undefined'.
1+
tests/cases/conformance/esnext/logicalAssignment/logicalAssignment4.ts(39,13): error TS2532: Object is possibly 'undefined'.
2+
tests/cases/conformance/esnext/logicalAssignment/logicalAssignment4.ts(45,13): error TS2532: Object is possibly 'undefined'.
23

34

4-
==== tests/cases/conformance/esnext/logicalAssignment/logicalAssignment4.ts (1 errors) ====
5+
==== tests/cases/conformance/esnext/logicalAssignment/logicalAssignment4.ts (2 errors) ====
56
function foo1(results: number[] | undefined) {
67
(results ||= []).push(100);
78
}
@@ -24,10 +25,33 @@ tests/cases/conformance/esnext/logicalAssignment/logicalAssignment4.ts(25,21): e
2425
name: string;
2526
original?: ThingWithOriginal
2627
}
27-
function doSomethingWithAlias(thing?: ThingWithOriginal | undefined) {
28-
if (thing &&= thing.original) {
29-
console.log(thing.name);
30-
~~~~~
28+
declare const v: number
29+
function doSomethingWithAlias(thing: ThingWithOriginal | undefined, defaultValue: ThingWithOriginal | undefined) {
30+
if (v === 1) {
31+
if (thing &&= thing.original) {
32+
thing.name;
33+
}
34+
}
35+
else if (v === 2) {
36+
if (thing &&= defaultValue) {
37+
thing.name;
38+
defaultValue.name
39+
}
40+
}
41+
else if (v === 3) {
42+
if (thing ||= defaultValue) {
43+
thing.name;
44+
defaultValue.name;
45+
~~~~~~~~~~~~
46+
!!! error TS2532: Object is possibly 'undefined'.
47+
}
48+
}
49+
else {
50+
if (thing ??= defaultValue) {
51+
thing.name;
52+
defaultValue.name;
53+
~~~~~~~~~~~~
3154
!!! error TS2532: Object is possibly 'undefined'.
55+
}
3256
}
3357
}

0 commit comments

Comments
 (0)