Skip to content

Commit 463c794

Browse files
authored
fix(43030): fix instantiated null/undefined type from JS initializer (#43933)
1 parent bb6d8a7 commit 463c794

15 files changed

+366
-22
lines changed

src/compiler/checker.ts

+6-2
Original file line numberDiff line numberDiff line change
@@ -20025,9 +20025,13 @@ namespace ts {
2002520025
return (type as TypeReference).cachedEquivalentBaseType = instantiatedBase;
2002620026
}
2002720027

20028+
function isEmptyLiteralType(type: Type): boolean {
20029+
return strictNullChecks ? type === implicitNeverType : type === undefinedWideningType;
20030+
}
20031+
2002820032
function isEmptyArrayLiteralType(type: Type): boolean {
2002920033
const elementType = getElementTypeOfArrayType(type);
20030-
return strictNullChecks ? elementType === implicitNeverType : elementType === undefinedWideningType;
20034+
return !!elementType && isEmptyLiteralType(elementType);
2003120035
}
2003220036

2003320037
function isTupleLikeType(type: Type): boolean {
@@ -32355,7 +32359,7 @@ namespace ts {
3235532359
function widenTypeInferredFromInitializer(declaration: HasExpressionInitializer, type: Type) {
3235632360
const widened = getCombinedNodeFlags(declaration) & NodeFlags.Const || isDeclarationReadonly(declaration) ? type : getWidenedLiteralType(type);
3235732361
if (isInJSFile(declaration)) {
32358-
if (widened.flags & TypeFlags.Nullable) {
32362+
if (isEmptyLiteralType(widened)) {
3235932363
reportImplicitAny(declaration, anyType);
3236032364
return anyType;
3236132365
}

tests/baselines/reference/typeFromJSInitializer.errors.txt

+18-6
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
11
tests/cases/conformance/salsa/a.js(3,5): error TS7008: Member 'unknown' implicitly has an 'any' type.
22
tests/cases/conformance/salsa/a.js(4,5): error TS7008: Member 'unknowable' implicitly has an 'any' type.
33
tests/cases/conformance/salsa/a.js(5,5): error TS7008: Member 'empty' implicitly has an 'any[]' type.
4-
tests/cases/conformance/salsa/a.js(25,12): error TS7006: Parameter 'a' implicitly has an 'any' type.
54
tests/cases/conformance/salsa/a.js(25,29): error TS7006: Parameter 'l' implicitly has an 'any[]' type.
5+
tests/cases/conformance/salsa/a.js(27,5): error TS2322: Type 'undefined' is not assignable to type 'null'.
6+
tests/cases/conformance/salsa/a.js(29,5): error TS2322: Type '1' is not assignable to type 'null'.
7+
tests/cases/conformance/salsa/a.js(30,5): error TS2322: Type 'true' is not assignable to type 'null'.
8+
tests/cases/conformance/salsa/a.js(31,5): error TS2322: Type '{}' is not assignable to type 'null'.
9+
tests/cases/conformance/salsa/a.js(32,5): error TS2322: Type '"ok"' is not assignable to type 'null'.
610
tests/cases/conformance/salsa/a.js(37,5): error TS2322: Type '"error"' is not assignable to type 'number | undefined'.
711

812

9-
==== tests/cases/conformance/salsa/a.js (6 errors) ====
13+
==== tests/cases/conformance/salsa/a.js (10 errors) ====
1014
function A () {
1115
// should get any on this-assignments in constructor
1216
this.unknown = null
@@ -38,17 +42,25 @@ tests/cases/conformance/salsa/a.js(37,5): error TS2322: Type '"error"' is not as
3842

3943
// should get any on parameter initialisers
4044
function f(a = null, b = n, l = []) {
41-
~~~~~~~~
42-
!!! error TS7006: Parameter 'a' implicitly has an 'any' type.
4345
~~~~~~
4446
!!! error TS7006: Parameter 'l' implicitly has an 'any[]' type.
45-
// a should be any
47+
// a should be null in strict mode
4648
a = undefined
49+
~
50+
!!! error TS2322: Type 'undefined' is not assignable to type 'null'.
4751
a = null
4852
a = 1
53+
~
54+
!!! error TS2322: Type '1' is not assignable to type 'null'.
4955
a = true
56+
~
57+
!!! error TS2322: Type 'true' is not assignable to type 'null'.
5058
a = {}
59+
~
60+
!!! error TS2322: Type '{}' is not assignable to type 'null'.
5161
a = 'ok'
62+
~
63+
!!! error TS2322: Type '"ok"' is not assignable to type 'null'.
5264

5365
// b should be number | undefined, not any
5466
b = 1
@@ -77,6 +89,6 @@ tests/cases/conformance/salsa/a.js(37,5): error TS2322: Type '"error"' is not as
7789
const isUndef = v => v === undefined;
7890
const e = [1, undefined];
7991

80-
// should be undefined[]
92+
// should be undefined[]
8193
const g = e.filter(isUndef);
8294

tests/baselines/reference/typeFromJSInitializer.symbols

+2-2
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ function f(a = null, b = n, l = []) {
103103
>n : Symbol(n, Decl(a.js, 21, 3))
104104
>l : Symbol(l, Decl(a.js, 24, 27))
105105

106-
// a should be any
106+
// a should be null in strict mode
107107
a = undefined
108108
>a : Symbol(a, Decl(a.js, 24, 11))
109109
>undefined : Symbol(undefined)
@@ -186,7 +186,7 @@ const e = [1, undefined];
186186
>e : Symbol(e, Decl(a.js, 56, 5))
187187
>undefined : Symbol(undefined)
188188

189-
// should be undefined[]
189+
// should be undefined[]
190190
const g = e.filter(isUndef);
191191
>g : Symbol(g, Decl(a.js, 59, 5))
192192
>e.filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))

tests/baselines/reference/typeFromJSInitializer.types

+10-10
Original file line numberDiff line numberDiff line change
@@ -127,43 +127,43 @@ var n;
127127

128128
// should get any on parameter initialisers
129129
function f(a = null, b = n, l = []) {
130-
>f : (a?: any, b?: number | undefined, l?: any[]) => void
131-
>a : any
130+
>f : (a?: null, b?: number | undefined, l?: any[]) => void
131+
>a : null
132132
>null : null
133133
>b : number | undefined
134134
>n : number | undefined
135135
>l : any[]
136136
>[] : never[]
137137

138-
// a should be any
138+
// a should be null in strict mode
139139
a = undefined
140140
>a = undefined : undefined
141-
>a : any
141+
>a : null
142142
>undefined : undefined
143143

144144
a = null
145145
>a = null : null
146-
>a : any
146+
>a : null
147147
>null : null
148148

149149
a = 1
150150
>a = 1 : 1
151-
>a : any
151+
>a : null
152152
>1 : 1
153153

154154
a = true
155155
>a = true : true
156-
>a : any
156+
>a : null
157157
>true : true
158158

159159
a = {}
160160
>a = {} : {}
161-
>a : any
161+
>a : null
162162
>{} : {}
163163

164164
a = 'ok'
165165
>a = 'ok' : "ok"
166-
>a : any
166+
>a : null
167167
>'ok' : "ok"
168168

169169
// b should be number | undefined, not any
@@ -254,7 +254,7 @@ const e = [1, undefined];
254254
>1 : 1
255255
>undefined : undefined
256256

257-
// should be undefined[]
257+
// should be undefined[]
258258
const g = e.filter(isUndef);
259259
>g : undefined[]
260260
>e.filter(isUndef) : undefined[]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
=== tests/cases/conformance/salsa/a.js ===
2+
/** @type {() => undefined} */
3+
function f1() {
4+
>f1 : Symbol(f1, Decl(a.js, 0, 0))
5+
6+
return undefined;
7+
>undefined : Symbol(undefined)
8+
}
9+
const a = f1()
10+
>a : Symbol(a, Decl(a.js, 4, 5))
11+
>f1 : Symbol(f1, Decl(a.js, 0, 0))
12+
13+
/** @type {() => null} */
14+
function f2() {
15+
>f2 : Symbol(f2, Decl(a.js, 4, 14))
16+
17+
return null;
18+
}
19+
const b = f2()
20+
>b : Symbol(b, Decl(a.js, 10, 5))
21+
>f2 : Symbol(f2, Decl(a.js, 4, 14))
22+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
=== tests/cases/conformance/salsa/a.js ===
2+
/** @type {() => undefined} */
3+
function f1() {
4+
>f1 : () => undefined
5+
6+
return undefined;
7+
>undefined : undefined
8+
}
9+
const a = f1()
10+
>a : undefined
11+
>f1() : undefined
12+
>f1 : () => undefined
13+
14+
/** @type {() => null} */
15+
function f2() {
16+
>f2 : () => null
17+
18+
return null;
19+
>null : null
20+
}
21+
const b = f2()
22+
>b : null
23+
>f2() : null
24+
>f2 : () => null
25+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
=== tests/cases/conformance/salsa/a.js ===
2+
/** @type {() => undefined} */
3+
function f1() {
4+
>f1 : Symbol(f1, Decl(a.js, 0, 0))
5+
6+
return undefined;
7+
>undefined : Symbol(undefined)
8+
}
9+
const a = f1()
10+
>a : Symbol(a, Decl(a.js, 4, 5))
11+
>f1 : Symbol(f1, Decl(a.js, 0, 0))
12+
13+
/** @type {() => null} */
14+
function f2() {
15+
>f2 : Symbol(f2, Decl(a.js, 4, 14))
16+
17+
return null;
18+
}
19+
const b = f2()
20+
>b : Symbol(b, Decl(a.js, 10, 5))
21+
>f2 : Symbol(f2, Decl(a.js, 4, 14))
22+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
=== tests/cases/conformance/salsa/a.js ===
2+
/** @type {() => undefined} */
3+
function f1() {
4+
>f1 : () => undefined
5+
6+
return undefined;
7+
>undefined : undefined
8+
}
9+
const a = f1()
10+
>a : undefined
11+
>f1() : undefined
12+
>f1 : () => undefined
13+
14+
/** @type {() => null} */
15+
function f2() {
16+
>f2 : () => null
17+
18+
return null;
19+
>null : null
20+
}
21+
const b = f2()
22+
>b : null
23+
>f2() : null
24+
>f2 : () => null
25+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
tests/cases/conformance/salsa/a.js(5,12): error TS7006: Parameter 'a' implicitly has an 'any' type.
2+
tests/cases/conformance/salsa/a.js(5,29): error TS7006: Parameter 'l' implicitly has an 'any[]' type.
3+
tests/cases/conformance/salsa/a.js(17,5): error TS2322: Type 'string' is not assignable to type 'number'.
4+
5+
6+
==== tests/cases/conformance/salsa/a.js (3 errors) ====
7+
/** @type {number | undefined} */
8+
var n;
9+
10+
// should get any on parameter initialisers
11+
function f(a = null, b = n, l = []) {
12+
~~~~~~~~
13+
!!! error TS7006: Parameter 'a' implicitly has an 'any' type.
14+
~~~~~~
15+
!!! error TS7006: Parameter 'l' implicitly has an 'any[]' type.
16+
// a should be any
17+
a = undefined
18+
a = null
19+
a = 1
20+
a = true
21+
a = {}
22+
a = 'ok'
23+
24+
// b should be number | undefined, not any
25+
b = 1
26+
b = undefined
27+
b = 'error'
28+
~
29+
!!! error TS2322: Type 'string' is not assignable to type 'number'.
30+
31+
// l should be any[]
32+
l.push(1)
33+
l.push('ok')
34+
}
35+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
=== tests/cases/conformance/salsa/a.js ===
2+
/** @type {number | undefined} */
3+
var n;
4+
>n : Symbol(n, Decl(a.js, 1, 3))
5+
6+
// should get any on parameter initialisers
7+
function f(a = null, b = n, l = []) {
8+
>f : Symbol(f, Decl(a.js, 1, 6))
9+
>a : Symbol(a, Decl(a.js, 4, 11))
10+
>b : Symbol(b, Decl(a.js, 4, 20))
11+
>n : Symbol(n, Decl(a.js, 1, 3))
12+
>l : Symbol(l, Decl(a.js, 4, 27))
13+
14+
// a should be any
15+
a = undefined
16+
>a : Symbol(a, Decl(a.js, 4, 11))
17+
>undefined : Symbol(undefined)
18+
19+
a = null
20+
>a : Symbol(a, Decl(a.js, 4, 11))
21+
22+
a = 1
23+
>a : Symbol(a, Decl(a.js, 4, 11))
24+
25+
a = true
26+
>a : Symbol(a, Decl(a.js, 4, 11))
27+
28+
a = {}
29+
>a : Symbol(a, Decl(a.js, 4, 11))
30+
31+
a = 'ok'
32+
>a : Symbol(a, Decl(a.js, 4, 11))
33+
34+
// b should be number | undefined, not any
35+
b = 1
36+
>b : Symbol(b, Decl(a.js, 4, 20))
37+
38+
b = undefined
39+
>b : Symbol(b, Decl(a.js, 4, 20))
40+
>undefined : Symbol(undefined)
41+
42+
b = 'error'
43+
>b : Symbol(b, Decl(a.js, 4, 20))
44+
45+
// l should be any[]
46+
l.push(1)
47+
>l.push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --))
48+
>l : Symbol(l, Decl(a.js, 4, 27))
49+
>push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --))
50+
51+
l.push('ok')
52+
>l.push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --))
53+
>l : Symbol(l, Decl(a.js, 4, 27))
54+
>push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --))
55+
}
56+

0 commit comments

Comments
 (0)