fix(43030): fix instantiated null/undefined type from JS initializer (#43933)

This commit is contained in:
Oleksandr T 2021-05-12 01:57:16 +03:00 committed by GitHub
parent bb6d8a716e
commit 463c79440f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 366 additions and 22 deletions

View file

@ -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 {
@ -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;
}

View file

@ -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
@ -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
@ -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);

View file

@ -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)
@ -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, --, --))

View file

@ -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
@ -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[]

View file

@ -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))

View file

@ -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

View file

@ -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))

View file

@ -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

View file

@ -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')
}

View file

@ -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, --, --))
}

View file

@ -0,0 +1,78 @@
=== tests/cases/conformance/salsa/a.js ===
/** @type {number | undefined} */
var n;
>n : number
// should get any on parameter initialisers
function f(a = null, b = n, l = []) {
>f : (a?: any, b?: number, l?: any[]) => void
>a : any
>null : null
>b : number
>n : number
>l : any[]
>[] : undefined[]
// a should be any
a = undefined
>a = undefined : undefined
>a : any
>undefined : undefined
a = null
>a = null : null
>a : any
>null : null
a = 1
>a = 1 : 1
>a : any
>1 : 1
a = true
>a = true : true
>a : any
>true : true
a = {}
>a = {} : {}
>a : any
>{} : {}
a = 'ok'
>a = 'ok' : "ok"
>a : any
>'ok' : "ok"
// b should be number | undefined, not any
b = 1
>b = 1 : 1
>b : number
>1 : 1
b = undefined
>b = undefined : undefined
>b : number
>undefined : undefined
b = 'error'
>b = 'error' : "error"
>b : number
>'error' : "error"
// l should be any[]
l.push(1)
>l.push(1) : number
>l.push : (...items: any[]) => number
>l : any[]
>push : (...items: any[]) => number
>1 : 1
l.push('ok')
>l.push('ok') : number
>l.push : (...items: any[]) => number
>l : any[]
>push : (...items: any[]) => number
>'ok' : "ok"
}

View file

@ -29,7 +29,7 @@ var n;
// should get any on parameter initialisers
function f(a = null, b = n, l = []) {
// a should be any
// a should be null in strict mode
a = undefined
a = null
a = 1
@ -62,5 +62,5 @@ l.push('ok')
const isUndef = v => v === undefined;
const e = [1, undefined];
// should be undefined[]
// should be undefined[]
const g = e.filter(isUndef);

View file

@ -0,0 +1,18 @@
// @allowJs: true
// @checkJs: true
// @noEmit: true
// @strictNullChecks: true
// @noImplicitAny: true
// @Filename: a.js
/** @type {() => undefined} */
function f1() {
return undefined;
}
const a = f1()
/** @type {() => null} */
function f2() {
return null;
}
const b = f2()

View file

@ -0,0 +1,18 @@
// @allowJs: true
// @checkJs: true
// @noEmit: true
// @strictNullChecks: false
// @noImplicitAny: true
// @Filename: a.js
/** @type {() => undefined} */
function f1() {
return undefined;
}
const a = f1()
/** @type {() => null} */
function f2() {
return null;
}
const b = f2()

View file

@ -0,0 +1,29 @@
// @allowJs: true
// @checkJs: true
// @noEmit: true
// @strictNullChecks: false
// @noImplicitAny: true
// @Filename: a.js
/** @type {number | undefined} */
var n;
// should get any on parameter initialisers
function f(a = null, b = n, l = []) {
// 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'
// l should be any[]
l.push(1)
l.push('ok')
}