Skip to content

Commit 83bc70d

Browse files
authored
Merge pull request #23782 from Microsoft/controlFlowNullTypeAndLiteral
Control flow for null type vs. literal
2 parents 33ba250 + a286cff commit 83bc70d

6 files changed

+287
-1
lines changed

src/compiler/checker.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13690,7 +13690,7 @@ namespace ts {
1369013690
const doubleEquals = operator === SyntaxKind.EqualsEqualsToken || operator === SyntaxKind.ExclamationEqualsToken;
1369113691
const facts = doubleEquals ?
1369213692
assumeTrue ? TypeFacts.EQUndefinedOrNull : TypeFacts.NEUndefinedOrNull :
13693-
value.kind === SyntaxKind.NullKeyword ?
13693+
valueType.flags & TypeFlags.Null ?
1369413694
assumeTrue ? TypeFacts.EQNull : TypeFacts.NENull :
1369513695
assumeTrue ? TypeFacts.EQUndefined : TypeFacts.NEUndefined;
1369613696
return getTypeWithFacts(type, facts);
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
tests/cases/compiler/controlFlowNullTypeAndLiteral.ts(15,12): error TS2345: Argument of type 'null' is not assignable to parameter of type 'number'.
2+
tests/cases/compiler/controlFlowNullTypeAndLiteral.ts(17,12): error TS2345: Argument of type 'null' is not assignable to parameter of type 'number'.
3+
tests/cases/compiler/controlFlowNullTypeAndLiteral.ts(21,15): error TS2322: Type 'null' is not assignable to type 'string'.
4+
5+
6+
==== tests/cases/compiler/controlFlowNullTypeAndLiteral.ts (3 errors) ====
7+
// Repros from #23771
8+
9+
const myNull: null = null;
10+
const objWithValMaybeNull: { val: number | null } = { val: 1 };
11+
const addOne = function (num: number) {
12+
return num + 1;
13+
}
14+
15+
if (objWithValMaybeNull.val !== null)
16+
addOne(objWithValMaybeNull.val);
17+
if (objWithValMaybeNull.val !== myNull)
18+
addOne(objWithValMaybeNull.val);
19+
20+
if (objWithValMaybeNull.val === null)
21+
addOne(objWithValMaybeNull.val); // Error
22+
~~~~~~~~~~~~~~~~~~~~~~~
23+
!!! error TS2345: Argument of type 'null' is not assignable to parameter of type 'number'.
24+
if (objWithValMaybeNull.val === myNull)
25+
addOne(objWithValMaybeNull.val); // Error
26+
~~~~~~~~~~~~~~~~~~~~~~~
27+
!!! error TS2345: Argument of type 'null' is not assignable to parameter of type 'number'.
28+
29+
function f(x: number | null) {
30+
if(x === myNull) {
31+
const s: string = x; // Error
32+
~
33+
!!! error TS2322: Type 'null' is not assignable to type 'string'.
34+
}
35+
}
36+
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//// [controlFlowNullTypeAndLiteral.ts]
2+
// Repros from #23771
3+
4+
const myNull: null = null;
5+
const objWithValMaybeNull: { val: number | null } = { val: 1 };
6+
const addOne = function (num: number) {
7+
return num + 1;
8+
}
9+
10+
if (objWithValMaybeNull.val !== null)
11+
addOne(objWithValMaybeNull.val);
12+
if (objWithValMaybeNull.val !== myNull)
13+
addOne(objWithValMaybeNull.val);
14+
15+
if (objWithValMaybeNull.val === null)
16+
addOne(objWithValMaybeNull.val); // Error
17+
if (objWithValMaybeNull.val === myNull)
18+
addOne(objWithValMaybeNull.val); // Error
19+
20+
function f(x: number | null) {
21+
if(x === myNull) {
22+
const s: string = x; // Error
23+
}
24+
}
25+
26+
27+
//// [controlFlowNullTypeAndLiteral.js]
28+
"use strict";
29+
// Repros from #23771
30+
var myNull = null;
31+
var objWithValMaybeNull = { val: 1 };
32+
var addOne = function (num) {
33+
return num + 1;
34+
};
35+
if (objWithValMaybeNull.val !== null)
36+
addOne(objWithValMaybeNull.val);
37+
if (objWithValMaybeNull.val !== myNull)
38+
addOne(objWithValMaybeNull.val);
39+
if (objWithValMaybeNull.val === null)
40+
addOne(objWithValMaybeNull.val); // Error
41+
if (objWithValMaybeNull.val === myNull)
42+
addOne(objWithValMaybeNull.val); // Error
43+
function f(x) {
44+
if (x === myNull) {
45+
var s = x; // Error
46+
}
47+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
=== tests/cases/compiler/controlFlowNullTypeAndLiteral.ts ===
2+
// Repros from #23771
3+
4+
const myNull: null = null;
5+
>myNull : Symbol(myNull, Decl(controlFlowNullTypeAndLiteral.ts, 2, 5))
6+
7+
const objWithValMaybeNull: { val: number | null } = { val: 1 };
8+
>objWithValMaybeNull : Symbol(objWithValMaybeNull, Decl(controlFlowNullTypeAndLiteral.ts, 3, 5))
9+
>val : Symbol(val, Decl(controlFlowNullTypeAndLiteral.ts, 3, 28))
10+
>val : Symbol(val, Decl(controlFlowNullTypeAndLiteral.ts, 3, 53))
11+
12+
const addOne = function (num: number) {
13+
>addOne : Symbol(addOne, Decl(controlFlowNullTypeAndLiteral.ts, 4, 5))
14+
>num : Symbol(num, Decl(controlFlowNullTypeAndLiteral.ts, 4, 25))
15+
16+
return num + 1;
17+
>num : Symbol(num, Decl(controlFlowNullTypeAndLiteral.ts, 4, 25))
18+
}
19+
20+
if (objWithValMaybeNull.val !== null)
21+
>objWithValMaybeNull.val : Symbol(val, Decl(controlFlowNullTypeAndLiteral.ts, 3, 28))
22+
>objWithValMaybeNull : Symbol(objWithValMaybeNull, Decl(controlFlowNullTypeAndLiteral.ts, 3, 5))
23+
>val : Symbol(val, Decl(controlFlowNullTypeAndLiteral.ts, 3, 28))
24+
25+
addOne(objWithValMaybeNull.val);
26+
>addOne : Symbol(addOne, Decl(controlFlowNullTypeAndLiteral.ts, 4, 5))
27+
>objWithValMaybeNull.val : Symbol(val, Decl(controlFlowNullTypeAndLiteral.ts, 3, 28))
28+
>objWithValMaybeNull : Symbol(objWithValMaybeNull, Decl(controlFlowNullTypeAndLiteral.ts, 3, 5))
29+
>val : Symbol(val, Decl(controlFlowNullTypeAndLiteral.ts, 3, 28))
30+
31+
if (objWithValMaybeNull.val !== myNull)
32+
>objWithValMaybeNull.val : Symbol(val, Decl(controlFlowNullTypeAndLiteral.ts, 3, 28))
33+
>objWithValMaybeNull : Symbol(objWithValMaybeNull, Decl(controlFlowNullTypeAndLiteral.ts, 3, 5))
34+
>val : Symbol(val, Decl(controlFlowNullTypeAndLiteral.ts, 3, 28))
35+
>myNull : Symbol(myNull, Decl(controlFlowNullTypeAndLiteral.ts, 2, 5))
36+
37+
addOne(objWithValMaybeNull.val);
38+
>addOne : Symbol(addOne, Decl(controlFlowNullTypeAndLiteral.ts, 4, 5))
39+
>objWithValMaybeNull.val : Symbol(val, Decl(controlFlowNullTypeAndLiteral.ts, 3, 28))
40+
>objWithValMaybeNull : Symbol(objWithValMaybeNull, Decl(controlFlowNullTypeAndLiteral.ts, 3, 5))
41+
>val : Symbol(val, Decl(controlFlowNullTypeAndLiteral.ts, 3, 28))
42+
43+
if (objWithValMaybeNull.val === null)
44+
>objWithValMaybeNull.val : Symbol(val, Decl(controlFlowNullTypeAndLiteral.ts, 3, 28))
45+
>objWithValMaybeNull : Symbol(objWithValMaybeNull, Decl(controlFlowNullTypeAndLiteral.ts, 3, 5))
46+
>val : Symbol(val, Decl(controlFlowNullTypeAndLiteral.ts, 3, 28))
47+
48+
addOne(objWithValMaybeNull.val); // Error
49+
>addOne : Symbol(addOne, Decl(controlFlowNullTypeAndLiteral.ts, 4, 5))
50+
>objWithValMaybeNull.val : Symbol(val, Decl(controlFlowNullTypeAndLiteral.ts, 3, 28))
51+
>objWithValMaybeNull : Symbol(objWithValMaybeNull, Decl(controlFlowNullTypeAndLiteral.ts, 3, 5))
52+
>val : Symbol(val, Decl(controlFlowNullTypeAndLiteral.ts, 3, 28))
53+
54+
if (objWithValMaybeNull.val === myNull)
55+
>objWithValMaybeNull.val : Symbol(val, Decl(controlFlowNullTypeAndLiteral.ts, 3, 28))
56+
>objWithValMaybeNull : Symbol(objWithValMaybeNull, Decl(controlFlowNullTypeAndLiteral.ts, 3, 5))
57+
>val : Symbol(val, Decl(controlFlowNullTypeAndLiteral.ts, 3, 28))
58+
>myNull : Symbol(myNull, Decl(controlFlowNullTypeAndLiteral.ts, 2, 5))
59+
60+
addOne(objWithValMaybeNull.val); // Error
61+
>addOne : Symbol(addOne, Decl(controlFlowNullTypeAndLiteral.ts, 4, 5))
62+
>objWithValMaybeNull.val : Symbol(val, Decl(controlFlowNullTypeAndLiteral.ts, 3, 28))
63+
>objWithValMaybeNull : Symbol(objWithValMaybeNull, Decl(controlFlowNullTypeAndLiteral.ts, 3, 5))
64+
>val : Symbol(val, Decl(controlFlowNullTypeAndLiteral.ts, 3, 28))
65+
66+
function f(x: number | null) {
67+
>f : Symbol(f, Decl(controlFlowNullTypeAndLiteral.ts, 16, 36))
68+
>x : Symbol(x, Decl(controlFlowNullTypeAndLiteral.ts, 18, 11))
69+
70+
if(x === myNull) {
71+
>x : Symbol(x, Decl(controlFlowNullTypeAndLiteral.ts, 18, 11))
72+
>myNull : Symbol(myNull, Decl(controlFlowNullTypeAndLiteral.ts, 2, 5))
73+
74+
const s: string = x; // Error
75+
>s : Symbol(s, Decl(controlFlowNullTypeAndLiteral.ts, 20, 13))
76+
>x : Symbol(x, Decl(controlFlowNullTypeAndLiteral.ts, 18, 11))
77+
}
78+
}
79+
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
=== tests/cases/compiler/controlFlowNullTypeAndLiteral.ts ===
2+
// Repros from #23771
3+
4+
const myNull: null = null;
5+
>myNull : null
6+
>null : null
7+
>null : null
8+
9+
const objWithValMaybeNull: { val: number | null } = { val: 1 };
10+
>objWithValMaybeNull : { val: number | null; }
11+
>val : number | null
12+
>null : null
13+
>{ val: 1 } : { val: number; }
14+
>val : number
15+
>1 : 1
16+
17+
const addOne = function (num: number) {
18+
>addOne : (num: number) => number
19+
>function (num: number) { return num + 1;} : (num: number) => number
20+
>num : number
21+
22+
return num + 1;
23+
>num + 1 : number
24+
>num : number
25+
>1 : 1
26+
}
27+
28+
if (objWithValMaybeNull.val !== null)
29+
>objWithValMaybeNull.val !== null : boolean
30+
>objWithValMaybeNull.val : number | null
31+
>objWithValMaybeNull : { val: number | null; }
32+
>val : number | null
33+
>null : null
34+
35+
addOne(objWithValMaybeNull.val);
36+
>addOne(objWithValMaybeNull.val) : number
37+
>addOne : (num: number) => number
38+
>objWithValMaybeNull.val : number
39+
>objWithValMaybeNull : { val: number | null; }
40+
>val : number
41+
42+
if (objWithValMaybeNull.val !== myNull)
43+
>objWithValMaybeNull.val !== myNull : boolean
44+
>objWithValMaybeNull.val : number | null
45+
>objWithValMaybeNull : { val: number | null; }
46+
>val : number | null
47+
>myNull : null
48+
49+
addOne(objWithValMaybeNull.val);
50+
>addOne(objWithValMaybeNull.val) : number
51+
>addOne : (num: number) => number
52+
>objWithValMaybeNull.val : number
53+
>objWithValMaybeNull : { val: number | null; }
54+
>val : number
55+
56+
if (objWithValMaybeNull.val === null)
57+
>objWithValMaybeNull.val === null : boolean
58+
>objWithValMaybeNull.val : number | null
59+
>objWithValMaybeNull : { val: number | null; }
60+
>val : number | null
61+
>null : null
62+
63+
addOne(objWithValMaybeNull.val); // Error
64+
>addOne(objWithValMaybeNull.val) : number
65+
>addOne : (num: number) => number
66+
>objWithValMaybeNull.val : null
67+
>objWithValMaybeNull : { val: number | null; }
68+
>val : null
69+
70+
if (objWithValMaybeNull.val === myNull)
71+
>objWithValMaybeNull.val === myNull : boolean
72+
>objWithValMaybeNull.val : number | null
73+
>objWithValMaybeNull : { val: number | null; }
74+
>val : number | null
75+
>myNull : null
76+
77+
addOne(objWithValMaybeNull.val); // Error
78+
>addOne(objWithValMaybeNull.val) : number
79+
>addOne : (num: number) => number
80+
>objWithValMaybeNull.val : null
81+
>objWithValMaybeNull : { val: number | null; }
82+
>val : null
83+
84+
function f(x: number | null) {
85+
>f : (x: number | null) => void
86+
>x : number | null
87+
>null : null
88+
89+
if(x === myNull) {
90+
>x === myNull : boolean
91+
>x : number | null
92+
>myNull : null
93+
94+
const s: string = x; // Error
95+
>s : string
96+
>x : null
97+
}
98+
}
99+
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// @strict: true
2+
3+
// Repros from #23771
4+
5+
const myNull: null = null;
6+
const objWithValMaybeNull: { val: number | null } = { val: 1 };
7+
const addOne = function (num: number) {
8+
return num + 1;
9+
}
10+
11+
if (objWithValMaybeNull.val !== null)
12+
addOne(objWithValMaybeNull.val);
13+
if (objWithValMaybeNull.val !== myNull)
14+
addOne(objWithValMaybeNull.val);
15+
16+
if (objWithValMaybeNull.val === null)
17+
addOne(objWithValMaybeNull.val); // Error
18+
if (objWithValMaybeNull.val === myNull)
19+
addOne(objWithValMaybeNull.val); // Error
20+
21+
function f(x: number | null) {
22+
if(x === myNull) {
23+
const s: string = x; // Error
24+
}
25+
}

0 commit comments

Comments
 (0)