Skip to content

Issue implicit any in JS files on widened inferred types from initializers in binding patterns #1064

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion internal/checker/checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -16077,6 +16077,21 @@ func (c *Checker) padTupleType(t *Type, pattern *ast.Node) *Type {
}

func (c *Checker) widenTypeInferredFromInitializer(declaration *ast.Node, t *Type) *Type {
widened := c.getWidenedLiteralTypeForInitializer(declaration, t)
if ast.IsInJSFile(declaration) {
if c.isEmptyLiteralType(widened) {
c.reportImplicitAny(declaration, c.anyType, WideningKindNormal)
return c.anyType
}
if c.isEmptyArrayLiteralType(widened) {
c.reportImplicitAny(declaration, c.anyArrayType, WideningKindNormal)
return c.anyArrayType
}
}
return widened
}

func (c *Checker) getWidenedLiteralTypeForInitializer(declaration *ast.Node, t *Type) *Type {
if c.getCombinedNodeFlagsCached(declaration)&ast.NodeFlagsConstant != 0 || isDeclarationReadonly(declaration) {
return t
}
Expand Down Expand Up @@ -17197,7 +17212,7 @@ func (c *Checker) getTypeFromBindingElement(element *ast.Node, includePatternInT
if ast.IsBindingPattern(element.Name()) {
contextualType = c.getTypeFromBindingPattern(element.Name(), true /*includePatternInType*/, false /*reportErrors*/)
}
return c.addOptionality(c.widenTypeInferredFromInitializer(element, c.checkDeclarationInitializer(element, CheckModeNormal, contextualType)))
return c.addOptionality(c.getWidenedLiteralTypeForInitializer(element, c.checkDeclarationInitializer(element, CheckModeNormal, contextualType)))
}
if ast.IsBindingPattern(element.Name()) {
return c.getTypeFromBindingPattern(element.Name(), includePatternInType, reportErrors)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
index.js(7,9): error TS7031: Binding element 'json' implicitly has an 'any[]' type.
index.js(15,9): error TS7031: Binding element 'json' implicitly has an 'any[]' type.
index.js(37,20): error TS7031: Binding element 'json' implicitly has an 'any[]' type.
index.js(42,15): error TS7031: Binding element 'json' implicitly has an 'any[]' type.


==== index.js (4 errors) ====
/**
* @param {Object} [config]
* @param {Partial<Record<'json' | 'jsonc' | 'json5', string[]>>} [config.additionalFiles]
*/
export function prepareConfig({
additionalFiles: {
json = []
~~~~
!!! error TS7031: Binding element 'json' implicitly has an 'any[]' type.
} = {}
} = {}) {
json // string[]
}

export function prepareConfigWithoutAnnotation({
additionalFiles: {
json = []
~~~~
!!! error TS7031: Binding element 'json' implicitly has an 'any[]' type.
} = {}
} = {}) {
json
}

/** @type {(param: {
additionalFiles?: Partial<Record<"json" | "jsonc" | "json5", string[]>>;
}) => void} */
export const prepareConfigWithContextualSignature = ({
additionalFiles: {
json = []
} = {}
} = {})=> {
json // string[]
}

// Additional repros from https://github.com/microsoft/TypeScript/issues/59936

/**
* @param {{ a?: { json?: string[] }}} [config]
*/
function f1({ a: { json = [] } = {} } = {}) { return json }
~~~~
!!! error TS7031: Binding element 'json' implicitly has an 'any[]' type.

/**
* @param {[[string[]?]?]} [x]
*/
function f2([[json = []] = []] = []) { return json }
~~~~
!!! error TS7031: Binding element 'json' implicitly has an 'any[]' type.

Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ export function prepareConfig({
>additionalFiles : any

json = []
>json : never[]
>json : any[]
>[] : never[]

} = {}
Expand All @@ -22,7 +22,7 @@ export function prepareConfig({
>{} : {}

json // string[]
>json : never[]
>json : any[]
}

export function prepareConfigWithoutAnnotation({
Expand All @@ -32,7 +32,7 @@ export function prepareConfigWithoutAnnotation({
>additionalFiles : any

json = []
>json : never[]
>json : any[]
>[] : never[]

} = {}
Expand All @@ -42,7 +42,7 @@ export function prepareConfigWithoutAnnotation({
>{} : {}

json
>json : never[]
>json : any[]
}

/** @type {(param: {
Expand Down Expand Up @@ -75,22 +75,22 @@ export const prepareConfigWithContextualSignature = ({
* @param {{ a?: { json?: string[] }}} [config]
*/
function f1({ a: { json = [] } = {} } = {}) { return json }
>f1 : ({ a: { json } }?: { a?: { json?: never[] | undefined; } | undefined; }) => never[]
>f1 : ({ a: { json } }?: { a?: { json?: never[] | undefined; } | undefined; }) => any[]
>a : any
>json : never[]
>json : any[]
>[] : never[]
>{} : {}
>{} : {}
>json : never[]
>json : any[]

/**
* @param {[[string[]?]?]} [x]
*/
function f2([[json = []] = []] = []) { return json }
>f2 : ([[json]]?: [([(never[] | undefined)?] | undefined)?]) => never[]
>json : never[]
>f2 : ([[json]]?: [([(never[] | undefined)?] | undefined)?]) => any[]
>json : any[]
>[] : never[]
>[] : []
>[] : []
>json : never[]
>json : any[]

Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
a.js(7,9): error TS7009: 'new' expression, whose target lacks a construct signature, implicitly has an 'any' type.
a.js(25,29): error TS7006: Parameter 'l' implicitly has an 'any[]' type.
a.js(27,5): error TS2322: Type 'undefined' is not assignable to type 'null'.
a.js(29,5): error TS2322: Type '1' is not assignable to type 'null'.
a.js(30,5): error TS2322: Type 'true' is not assignable to type 'null'.
a.js(31,5): error TS2322: Type '{}' is not assignable to type 'null'.
a.js(32,5): error TS2322: Type '"ok"' is not assignable to type 'null'.
a.js(37,5): error TS2322: Type 'string' is not assignable to type 'number'.
a.js(40,12): error TS2345: Argument of type '1' is not assignable to parameter of type 'never'.
a.js(41,12): error TS2345: Argument of type '"ok"' is not assignable to parameter of type 'never'.


==== a.js (9 errors) ====
==== a.js (8 errors) ====
function A () {
// should get any on this-assignments in constructor
this.unknown = null
Expand Down Expand Up @@ -37,6 +36,8 @@ a.js(41,12): error TS2345: Argument of type '"ok"' is not assignable to paramete

// should get any on parameter initialisers
function f(a = null, b = n, l = []) {
~~~~~~
!!! error TS7006: Parameter 'l' implicitly has an 'any[]' type.
// a should be null in strict mode
a = undefined
~
Expand Down Expand Up @@ -64,11 +65,7 @@ a.js(41,12): error TS2345: Argument of type '"ok"' is not assignable to paramete

// l should be any[]
l.push(1)
~
!!! error TS2345: Argument of type '1' is not assignable to parameter of type 'never'.
l.push('ok')
~~~~
!!! error TS2345: Argument of type '"ok"' is not assignable to parameter of type 'never'.
}

// should get any on variable initialisers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,11 +128,11 @@ var n;

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

// a should be null in strict mode
Expand Down Expand Up @@ -184,16 +184,16 @@ function f(a = null, b = n, l = []) {
// l should be any[]
l.push(1)
>l.push(1) : number
>l.push : (...items: never[]) => number
>l : never[]
>push : (...items: never[]) => number
>l.push : (...items: any[]) => number
>l : any[]
>push : (...items: any[]) => number
>1 : 1

l.push('ok')
>l.push('ok') : number
>l.push : (...items: never[]) => number
>l : never[]
>push : (...items: never[]) => number
>l.push : (...items: any[]) => number
>l : any[]
>push : (...items: any[]) => number
>'ok' : "ok"
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,53 +1,39 @@
--- old.destructuringParameterDeclaration9(strict=true).errors.txt
+++ new.destructuringParameterDeclaration9(strict=true).errors.txt
@@= skipped -0, +0 lines =@@
-index.js(15,9): error TS7031: Binding element 'json' implicitly has an 'any[]' type.
+index.js(7,9): error TS7031: Binding element 'json' implicitly has an 'any[]' type.
index.js(15,9): error TS7031: Binding element 'json' implicitly has an 'any[]' type.
-
-
-==== index.js (1 errors) ====
- /**
- * @param {Object} [config]
- * @param {Partial<Record<'json' | 'jsonc' | 'json5', string[]>>} [config.additionalFiles]
- */
- export function prepareConfig({
- additionalFiles: {
- json = []
- } = {}
- } = {}) {
- json // string[]
- }
-
- export function prepareConfigWithoutAnnotation({
- additionalFiles: {
- json = []
- ~~~~
-!!! error TS7031: Binding element 'json' implicitly has an 'any[]' type.
- } = {}
- } = {}) {
- json
- }
-
- /** @type {(param: {
- additionalFiles?: Partial<Record<"json" | "jsonc" | "json5", string[]>>;
- }) => void} */
- export const prepareConfigWithContextualSignature = ({
- additionalFiles: {
- json = []
- } = {}
- } = {})=> {
- json // string[]
- }
-
- // Additional repros from https://github.com/microsoft/TypeScript/issues/59936
-
- /**
- * @param {{ a?: { json?: string[] }}} [config]
- */
- function f1({ a: { json = [] } = {} } = {}) { return json }
-
- /**
- * @param {[[string[]?]?]} [x]
- */
- function f2([[json = []] = []] = []) { return json }
-
+<no content>
+index.js(37,20): error TS7031: Binding element 'json' implicitly has an 'any[]' type.
+index.js(42,15): error TS7031: Binding element 'json' implicitly has an 'any[]' type.
+
+
+==== index.js (4 errors) ====
/**
* @param {Object} [config]
* @param {Partial<Record<'json' | 'jsonc' | 'json5', string[]>>} [config.additionalFiles]
@@= skipped -8, +11 lines =@@
export function prepareConfig({
additionalFiles: {
json = []
+ ~~~~
+!!! error TS7031: Binding element 'json' implicitly has an 'any[]' type.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also tried porting this and hit these same issues. I'm pretty sure these are exactly the things that we are aiming to prevent, but now I think the fix doesn't quite work anymore?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll take a look at this tomorrow~ then

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR aims to issue implicit any error - and it does that... to some extent 😉 It is overly eager though. From what I can see, this one is still not handled by the reparser - and that's why we are seeing errors here. The compiler thinks it doesn't have a type annotation

} = {}
} = {}) {
json // string[]
@@= skipped -32, +34 lines =@@
* @param {{ a?: { json?: string[] }}} [config]
*/
function f1({ a: { json = [] } = {} } = {}) { return json }
+ ~~~~
+!!! error TS7031: Binding element 'json' implicitly has an 'any[]' type.

/**
* @param {[[string[]?]?]} [x]
*/
function f2([[json = []] = []] = []) { return json }
+ ~~~~
+!!! error TS7031: Binding element 'json' implicitly has an 'any[]' type.

Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

json = []
->json : string[]
+>json : never[]
+>json : any[]
>[] : never[]

} = {}
Expand All @@ -21,28 +21,11 @@

json // string[]
->json : string[]
+>json : never[]
+>json : any[]
}

export function prepareConfigWithoutAnnotation({
@@= skipped -10, +10 lines =@@
>additionalFiles : any

json = []
->json : any[]
+>json : never[]
>[] : never[]

} = {}
@@= skipped -10, +10 lines =@@
>{} : {}

json
->json : any[]
+>json : never[]
}

/** @type {(param: {
@@= skipped -27, +27 lines =@@
additionalFiles?: Partial<Record<"json" | "jsonc" | "json5", string[]>>;
}) => void} */
export const prepareConfigWithContextualSignature = ({
Expand All @@ -53,31 +36,31 @@

additionalFiles: {
>additionalFiles : any
@@= skipped -33, +33 lines =@@
@@= skipped -26, +26 lines =@@
* @param {{ a?: { json?: string[] }}} [config]
*/
function f1({ a: { json = [] } = {} } = {}) { return json }
->f1 : ({ a: { json } }?: { a?: { json?: string[]; }; }) => string[]
+>f1 : ({ a: { json } }?: { a?: { json?: never[] | undefined; } | undefined; }) => never[]
+>f1 : ({ a: { json } }?: { a?: { json?: never[] | undefined; } | undefined; }) => any[]
>a : any
->json : string[]
+>json : never[]
+>json : any[]
>[] : never[]
>{} : {}
>{} : {}
->json : string[]
+>json : never[]
+>json : any[]

/**
* @param {[[string[]?]?]} [x]
*/
function f2([[json = []] = []] = []) { return json }
->f2 : ([[json]]?: [[string[]?]?]) => string[]
->json : string[]
+>f2 : ([[json]]?: [([(never[] | undefined)?] | undefined)?]) => never[]
+>json : never[]
+>f2 : ([[json]]?: [([(never[] | undefined)?] | undefined)?]) => any[]
+>json : any[]
>[] : never[]
>[] : []
>[] : []
->json : string[]
+>json : never[]
+>json : any[]
Loading