Skip to content

Commit

Permalink
fix(43030): fix instantiated null/undefined type from JS initializer (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
a-tarasyuk committed May 11, 2021
1 parent bb6d8a7 commit 463c794
Show file tree
Hide file tree
Showing 15 changed files with 366 additions and 22 deletions.
8 changes: 6 additions & 2 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20025,9 +20025,13 @@ namespace ts {
return (type as TypeReference).cachedEquivalentBaseType = instantiatedBase;
}

function isEmptyLiteralType(type: Type): boolean {
return strictNullChecks ? type === implicitNeverType : type === undefinedWideningType;
}

function isEmptyArrayLiteralType(type: Type): boolean {
const elementType = getElementTypeOfArrayType(type);
return strictNullChecks ? elementType === implicitNeverType : elementType === undefinedWideningType;
return !!elementType && isEmptyLiteralType(elementType);
}

function isTupleLikeType(type: Type): boolean {
Expand Down Expand Up @@ -32355,7 +32359,7 @@ namespace ts {
function widenTypeInferredFromInitializer(declaration: HasExpressionInitializer, type: Type) {
const widened = getCombinedNodeFlags(declaration) & NodeFlags.Const || isDeclarationReadonly(declaration) ? type : getWidenedLiteralType(type);
if (isInJSFile(declaration)) {
if (widened.flags & TypeFlags.Nullable) {
if (isEmptyLiteralType(widened)) {
reportImplicitAny(declaration, anyType);
return anyType;
}
Expand Down
24 changes: 18 additions & 6 deletions tests/baselines/reference/typeFromJSInitializer.errors.txt
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
tests/cases/conformance/salsa/a.js(3,5): error TS7008: Member 'unknown' implicitly has an 'any' type.
tests/cases/conformance/salsa/a.js(4,5): error TS7008: Member 'unknowable' implicitly has an 'any' type.
tests/cases/conformance/salsa/a.js(5,5): error TS7008: Member 'empty' implicitly has an 'any[]' type.
tests/cases/conformance/salsa/a.js(25,12): error TS7006: Parameter 'a' implicitly has an 'any' type.
tests/cases/conformance/salsa/a.js(25,29): error TS7006: Parameter 'l' implicitly has an 'any[]' type.
tests/cases/conformance/salsa/a.js(27,5): error TS2322: Type 'undefined' is not assignable to type 'null'.
tests/cases/conformance/salsa/a.js(29,5): error TS2322: Type '1' is not assignable to type 'null'.
tests/cases/conformance/salsa/a.js(30,5): error TS2322: Type 'true' is not assignable to type 'null'.
tests/cases/conformance/salsa/a.js(31,5): error TS2322: Type '{}' is not assignable to type 'null'.
tests/cases/conformance/salsa/a.js(32,5): error TS2322: Type '"ok"' is not assignable to type 'null'.
tests/cases/conformance/salsa/a.js(37,5): error TS2322: Type '"error"' is not assignable to type 'number | undefined'.


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

// should get any on parameter initialisers
function f(a = null, b = n, l = []) {
~~~~~~~~
!!! error TS7006: Parameter 'a' implicitly has an 'any' type.
~~~~~~
!!! error TS7006: Parameter 'l' implicitly has an 'any[]' type.
// a should be any
// a should be null in strict mode
a = undefined
~
!!! error TS2322: Type 'undefined' is not assignable to type 'null'.
a = null
a = 1
~
!!! error TS2322: Type '1' is not assignable to type 'null'.
a = true
~
!!! error TS2322: Type 'true' is not assignable to type 'null'.
a = {}
~
!!! error TS2322: Type '{}' is not assignable to type 'null'.
a = 'ok'
~
!!! error TS2322: Type '"ok"' is not assignable to type 'null'.

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

// should be undefined[]
// should be undefined[]
const g = e.filter(isUndef);

4 changes: 2 additions & 2 deletions tests/baselines/reference/typeFromJSInitializer.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ function f(a = null, b = n, l = []) {
>n : Symbol(n, Decl(a.js, 21, 3))
>l : Symbol(l, Decl(a.js, 24, 27))

// a should be any
// a should be null in strict mode
a = undefined
>a : Symbol(a, Decl(a.js, 24, 11))
>undefined : Symbol(undefined)
Expand Down Expand Up @@ -186,7 +186,7 @@ const e = [1, undefined];
>e : Symbol(e, Decl(a.js, 56, 5))
>undefined : Symbol(undefined)

// should be undefined[]
// should be undefined[]
const g = e.filter(isUndef);
>g : Symbol(g, Decl(a.js, 59, 5))
>e.filter : Symbol(Array.filter, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --))
Expand Down
20 changes: 10 additions & 10 deletions tests/baselines/reference/typeFromJSInitializer.types
Original file line number Diff line number Diff line change
Expand Up @@ -127,43 +127,43 @@ var n;

// should get any on parameter initialisers
function f(a = null, b = n, l = []) {
>f : (a?: any, b?: number | undefined, l?: any[]) => void
>a : any
>f : (a?: null, b?: number | undefined, l?: any[]) => void
>a : null
>null : null
>b : number | undefined
>n : number | undefined
>l : any[]
>[] : never[]

// a should be any
// a should be null in strict mode
a = undefined
>a = undefined : undefined
>a : any
>a : null
>undefined : undefined

a = null
>a = null : null
>a : any
>a : null
>null : null

a = 1
>a = 1 : 1
>a : any
>a : null
>1 : 1

a = true
>a = true : true
>a : any
>a : null
>true : true

a = {}
>a = {} : {}
>a : any
>a : null
>{} : {}

a = 'ok'
>a = 'ok' : "ok"
>a : any
>a : null
>'ok' : "ok"

// b should be number | undefined, not any
Expand Down Expand Up @@ -254,7 +254,7 @@ const e = [1, undefined];
>1 : 1
>undefined : undefined

// should be undefined[]
// should be undefined[]
const g = e.filter(isUndef);
>g : undefined[]
>e.filter(isUndef) : undefined[]
Expand Down
22 changes: 22 additions & 0 deletions tests/baselines/reference/typeFromJSInitializer2.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
=== tests/cases/conformance/salsa/a.js ===
/** @type {() => undefined} */
function f1() {
>f1 : Symbol(f1, Decl(a.js, 0, 0))

return undefined;
>undefined : Symbol(undefined)
}
const a = f1()
>a : Symbol(a, Decl(a.js, 4, 5))
>f1 : Symbol(f1, Decl(a.js, 0, 0))

/** @type {() => null} */
function f2() {
>f2 : Symbol(f2, Decl(a.js, 4, 14))

return null;
}
const b = f2()
>b : Symbol(b, Decl(a.js, 10, 5))
>f2 : Symbol(f2, Decl(a.js, 4, 14))

25 changes: 25 additions & 0 deletions tests/baselines/reference/typeFromJSInitializer2.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
=== tests/cases/conformance/salsa/a.js ===
/** @type {() => undefined} */
function f1() {
>f1 : () => undefined

return undefined;
>undefined : undefined
}
const a = f1()
>a : undefined
>f1() : undefined
>f1 : () => undefined

/** @type {() => null} */
function f2() {
>f2 : () => null

return null;
>null : null
}
const b = f2()
>b : null
>f2() : null
>f2 : () => null

22 changes: 22 additions & 0 deletions tests/baselines/reference/typeFromJSInitializer3.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
=== tests/cases/conformance/salsa/a.js ===
/** @type {() => undefined} */
function f1() {
>f1 : Symbol(f1, Decl(a.js, 0, 0))

return undefined;
>undefined : Symbol(undefined)
}
const a = f1()
>a : Symbol(a, Decl(a.js, 4, 5))
>f1 : Symbol(f1, Decl(a.js, 0, 0))

/** @type {() => null} */
function f2() {
>f2 : Symbol(f2, Decl(a.js, 4, 14))

return null;
}
const b = f2()
>b : Symbol(b, Decl(a.js, 10, 5))
>f2 : Symbol(f2, Decl(a.js, 4, 14))

25 changes: 25 additions & 0 deletions tests/baselines/reference/typeFromJSInitializer3.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
=== tests/cases/conformance/salsa/a.js ===
/** @type {() => undefined} */
function f1() {
>f1 : () => undefined

return undefined;
>undefined : undefined
}
const a = f1()
>a : undefined
>f1() : undefined
>f1 : () => undefined

/** @type {() => null} */
function f2() {
>f2 : () => null

return null;
>null : null
}
const b = f2()
>b : null
>f2() : null
>f2 : () => null

35 changes: 35 additions & 0 deletions tests/baselines/reference/typeFromJSInitializer4.errors.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
tests/cases/conformance/salsa/a.js(5,12): error TS7006: Parameter 'a' implicitly has an 'any' type.
tests/cases/conformance/salsa/a.js(5,29): error TS7006: Parameter 'l' implicitly has an 'any[]' type.
tests/cases/conformance/salsa/a.js(17,5): error TS2322: Type 'string' is not assignable to type 'number'.


==== tests/cases/conformance/salsa/a.js (3 errors) ====
/** @type {number | undefined} */
var n;

// should get any on parameter initialisers
function f(a = null, b = n, l = []) {
~~~~~~~~
!!! error TS7006: Parameter 'a' implicitly has an 'any' type.
~~~~~~
!!! error TS7006: Parameter 'l' implicitly has an 'any[]' type.
// a should be any
a = undefined
a = null
a = 1
a = true
a = {}
a = 'ok'

// b should be number | undefined, not any
b = 1
b = undefined
b = 'error'
~
!!! error TS2322: Type 'string' is not assignable to type 'number'.

// l should be any[]
l.push(1)
l.push('ok')
}

56 changes: 56 additions & 0 deletions tests/baselines/reference/typeFromJSInitializer4.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
=== tests/cases/conformance/salsa/a.js ===
/** @type {number | undefined} */
var n;
>n : Symbol(n, Decl(a.js, 1, 3))

// should get any on parameter initialisers
function f(a = null, b = n, l = []) {
>f : Symbol(f, Decl(a.js, 1, 6))
>a : Symbol(a, Decl(a.js, 4, 11))
>b : Symbol(b, Decl(a.js, 4, 20))
>n : Symbol(n, Decl(a.js, 1, 3))
>l : Symbol(l, Decl(a.js, 4, 27))

// a should be any
a = undefined
>a : Symbol(a, Decl(a.js, 4, 11))
>undefined : Symbol(undefined)

a = null
>a : Symbol(a, Decl(a.js, 4, 11))

a = 1
>a : Symbol(a, Decl(a.js, 4, 11))

a = true
>a : Symbol(a, Decl(a.js, 4, 11))

a = {}
>a : Symbol(a, Decl(a.js, 4, 11))

a = 'ok'
>a : Symbol(a, Decl(a.js, 4, 11))

// b should be number | undefined, not any
b = 1
>b : Symbol(b, Decl(a.js, 4, 20))

b = undefined
>b : Symbol(b, Decl(a.js, 4, 20))
>undefined : Symbol(undefined)

b = 'error'
>b : Symbol(b, Decl(a.js, 4, 20))

// l should be any[]
l.push(1)
>l.push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --))
>l : Symbol(l, Decl(a.js, 4, 27))
>push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --))

l.push('ok')
>l.push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --))
>l : Symbol(l, Decl(a.js, 4, 27))
>push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --))
}

0 comments on commit 463c794

Please sign in to comment.