From 0603fcb08dc9ec5e7ca66d5af3e46dbb8c084cc2 Mon Sep 17 00:00:00 2001 From: fisker Date: Sun, 23 Nov 2025 14:17:07 +0800 Subject: [PATCH 01/10] chore: check `VariableDeclaration` syntax error against tsNode --- packages/typescript-estree/src/convert.ts | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index d578c2b012cb..2631cb401e23 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -985,28 +985,27 @@ export class Converter { case SyntaxKind.VariableDeclaration: { const definite = !!node.exclamationToken; - const init = this.convertChild(node.initializer); - const id = this.convertBindingNameWithTypeAnnotation( - node.name, - node.type, - node, - ); + if (definite) { - if (init) { + if (node.initializer) { this.#throwError( node, 'Declarations with initializers cannot also have definite assignment assertions.', ); - } else if ( - id.type !== AST_NODE_TYPES.Identifier || - !id.typeAnnotation - ) { + } else if (node.name.kind !== SyntaxKind.Identifier || !node.type) { this.#throwError( node, 'Declarations with definite assignment assertions must also have type annotations.', ); } } + + const init = this.convertChild(node.initializer); + const id = this.convertBindingNameWithTypeAnnotation( + node.name, + node.type, + node, + ); return this.createNode(node, { type: AST_NODE_TYPES.VariableDeclarator, definite, From ad1341f953dee5bf7527aec2ad1a66e47c58b5a4 Mon Sep 17 00:00:00 2001 From: fisker Date: Sun, 23 Nov 2025 15:09:50 +0800 Subject: [PATCH 02/10] Rename `hasExclamationToken` --- packages/typescript-estree/src/convert.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index 2631cb401e23..1eea802aaece 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -984,9 +984,9 @@ export class Converter { } case SyntaxKind.VariableDeclaration: { - const definite = !!node.exclamationToken; + const hasExclamationToken = !!node.exclamationToken; - if (definite) { + if (hasExclamationToken) { if (node.initializer) { this.#throwError( node, @@ -1008,7 +1008,7 @@ export class Converter { ); return this.createNode(node, { type: AST_NODE_TYPES.VariableDeclarator, - definite, + definite: hasExclamationToken, id, init, }); From 50bd39ff590f7e3c1c95223bffdf1a24e3da7e1a Mon Sep 17 00:00:00 2001 From: fisker Date: Sun, 23 Nov 2025 15:41:23 +0800 Subject: [PATCH 03/10] Fix `VariableDeclarationList` --- packages/typescript-estree/src/convert.ts | 45 +++++++++++++---------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index 1eea802aaece..4d280959e59b 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -1000,6 +1000,31 @@ export class Converter { } } + if (node.parent.kind === SyntaxKind.VariableDeclarationList) { + const variableDeclarationList = node.parent; + const kind = getDeclarationKind(variableDeclarationList); + + if ( + variableDeclarationList.parent.kind === SyntaxKind.ForInStatement || + variableDeclarationList.parent.kind === SyntaxKind.ForStatement + ) { + if (kind === 'using' || kind === 'await using') { + if (!node.initializer) { + this.#throwError( + node, + `'${kind}' declarations may not be initialized in for statement.`, + ); + } + if (node.name.kind !== SyntaxKind.Identifier) { + this.#throwError( + node.name, + `'${kind}' declarations may not have binding patterns.`, + ); + } + } + } + } + const init = this.convertChild(node.initializer); const id = this.convertBindingNameWithTypeAnnotation( node.name, @@ -1092,30 +1117,12 @@ export class Converter { // mostly for for-of, for-in case SyntaxKind.VariableDeclarationList: { - const result = this.createNode(node, { + return this.createNode(node, { type: AST_NODE_TYPES.VariableDeclaration, declarations: this.convertChildren(node.declarations), declare: false, kind: getDeclarationKind(node), }); - - if (result.kind === 'using' || result.kind === 'await using') { - node.declarations.forEach((declaration, i) => { - if (result.declarations[i].init != null) { - this.#throwError( - declaration, - `'${result.kind}' declarations may not be initialized in for statement.`, - ); - } - if (result.declarations[i].id.type !== AST_NODE_TYPES.Identifier) { - this.#throwError( - declaration.name, - `'${result.kind}' declarations may not have binding patterns.`, - ); - } - }); - } - return result; } // Expressions From fe1572f7b89cae86beb8001cae065710945021a5 Mon Sep 17 00:00:00 2001 From: fisker Date: Sun, 23 Nov 2025 15:43:23 +0800 Subject: [PATCH 04/10] Prepare for VariableStatement --- packages/typescript-estree/src/convert.ts | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index 4d280959e59b..4206bbdaac82 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -1023,6 +1023,11 @@ export class Converter { } } } + + if ( + variableDeclarationList.parent.kind === SyntaxKind.VariableStatement + ) { + } } const init = this.convertChild(node.initializer); @@ -1040,19 +1045,22 @@ export class Converter { } case SyntaxKind.VariableStatement: { - const result = this.createNode(node, { - type: AST_NODE_TYPES.VariableDeclaration, - declarations: this.convertChildren(node.declarationList.declarations), - declare: hasModifier(SyntaxKind.DeclareKeyword, node), - kind: getDeclarationKind(node.declarationList), - }); + const declarations = node.declarationList.declarations; - if (!result.declarations.length) { + if (!declarations.length) { this.#throwError( node, 'A variable declaration list must have at least one variable declarator.', ); } + + const result = this.createNode(node, { + type: AST_NODE_TYPES.VariableDeclaration, + declarations: this.convertChildren(declarations), + declare: hasModifier(SyntaxKind.DeclareKeyword, node), + kind: getDeclarationKind(node.declarationList), + }); + if (result.kind === 'using' || result.kind === 'await using') { node.declarationList.declarations.forEach((declaration, i) => { if (result.declarations[i].init == null) { From 04fe79ad0992af667fa65f6be808c5ccd1f1431b Mon Sep 17 00:00:00 2001 From: fisker Date: Sun, 23 Nov 2025 15:48:35 +0800 Subject: [PATCH 05/10] Fix `VariableStatement` --- packages/typescript-estree/src/convert.ts | 105 +++++++++++----------- 1 file changed, 53 insertions(+), 52 deletions(-) diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index 4206bbdaac82..a3f9faba28c1 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -1027,6 +1027,59 @@ export class Converter { if ( variableDeclarationList.parent.kind === SyntaxKind.VariableStatement ) { + const variableStatement = variableDeclarationList.parent; + + if (kind === 'using' || kind === 'await using') { + if (!node.initializer) { + this.#throwError( + node, + `'${kind}' declarations must be initialized.`, + ); + } + if (node.name.kind !== SyntaxKind.Identifier) { + this.#throwError( + node.name, + `'${kind}' declarations may not have binding patterns.`, + ); + } + } + + const hasDeclareKeyword = hasModifier( + SyntaxKind.DeclareKeyword, + variableStatement, + ); + + // Definite assignment only allowed for non-declare let and var + if ( + hasDeclareKeyword || + ['await using', 'const', 'using'].includes(kind) + ) { + if (hasExclamationToken) { + this.#throwError( + node, + `A definite assignment assertion '!' is not permitted in this context.`, + ); + } + } + + if (hasDeclareKeyword) { + if ( + node.initializer && + (['let', 'var'].includes(kind) || node.type) + ) { + this.#throwError( + node, + `Initializers are not permitted in ambient contexts.`, + ); + } + // Theoretically, only certain initializers are allowed for declare const, + // (TS1254: A 'const' initializer in an ambient context must be a string + // or numeric literal or literal enum reference.) but we just allow + // all expressions + } + // Note! No-declare does not mean the variable is not ambient, because + // it can be further nested in other declare contexts. Therefore we cannot + // check for const initializers. } } @@ -1061,58 +1114,6 @@ export class Converter { kind: getDeclarationKind(node.declarationList), }); - if (result.kind === 'using' || result.kind === 'await using') { - node.declarationList.declarations.forEach((declaration, i) => { - if (result.declarations[i].init == null) { - this.#throwError( - declaration, - `'${result.kind}' declarations must be initialized.`, - ); - } - if (result.declarations[i].id.type !== AST_NODE_TYPES.Identifier) { - this.#throwError( - declaration.name, - `'${result.kind}' declarations may not have binding patterns.`, - ); - } - }); - } - // Definite assignment only allowed for non-declare let and var - if ( - result.declare || - ['await using', 'const', 'using'].includes(result.kind) - ) { - node.declarationList.declarations.forEach((declaration, i) => { - if (result.declarations[i].definite) { - this.#throwError( - declaration, - `A definite assignment assertion '!' is not permitted in this context.`, - ); - } - }); - } - if (result.declare) { - node.declarationList.declarations.forEach((declaration, i) => { - if ( - result.declarations[i].init && - (['let', 'var'].includes(result.kind) || - result.declarations[i].id.typeAnnotation) - ) { - this.#throwError( - declaration, - `Initializers are not permitted in ambient contexts.`, - ); - } - }); - // Theoretically, only certain initializers are allowed for declare const, - // (TS1254: A 'const' initializer in an ambient context must be a string - // or numeric literal or literal enum reference.) but we just allow - // all expressions - } - // Note! No-declare does not mean the variable is not ambient, because - // it can be further nested in other declare contexts. Therefore we cannot - // check for const initializers. - /** * Semantically, decorators are not allowed on variable declarations, * Pre 4.8 TS would include them in the AST, so we did as well. From 0cf79f959a6160029dded0a85b9a449b6dc10cc3 Mon Sep 17 00:00:00 2001 From: fisker Date: Thu, 27 Nov 2025 02:38:02 +0800 Subject: [PATCH 06/10] Linting --- packages/typescript-estree/src/convert.ts | 75 ++++++++++++----------- 1 file changed, 38 insertions(+), 37 deletions(-) diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index a3f9faba28c1..606d46e19e34 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -1005,22 +1005,24 @@ export class Converter { const kind = getDeclarationKind(variableDeclarationList); if ( - variableDeclarationList.parent.kind === SyntaxKind.ForInStatement || - variableDeclarationList.parent.kind === SyntaxKind.ForStatement + (variableDeclarationList.parent.kind === + SyntaxKind.ForInStatement || + variableDeclarationList.parent.kind === + SyntaxKind.ForStatement) && + (kind === 'using' || kind === 'await using') ) { - if (kind === 'using' || kind === 'await using') { - if (!node.initializer) { - this.#throwError( - node, - `'${kind}' declarations may not be initialized in for statement.`, - ); - } - if (node.name.kind !== SyntaxKind.Identifier) { - this.#throwError( - node.name, - `'${kind}' declarations may not have binding patterns.`, - ); - } + if (!node.initializer) { + this.#throwError( + node, + `'${kind}' declarations may not be initialized in for statement.`, + ); + } + + if (node.name.kind !== SyntaxKind.Identifier) { + this.#throwError( + node.name, + `'${kind}' declarations may not have binding patterns.`, + ); } } @@ -1051,32 +1053,31 @@ export class Converter { // Definite assignment only allowed for non-declare let and var if ( - hasDeclareKeyword || - ['await using', 'const', 'using'].includes(kind) + (hasDeclareKeyword || + ['await using', 'const', 'using'].includes(kind)) && + hasExclamationToken ) { - if (hasExclamationToken) { - this.#throwError( - node, - `A definite assignment assertion '!' is not permitted in this context.`, - ); - } + this.#throwError( + node, + `A definite assignment assertion '!' is not permitted in this context.`, + ); } - if (hasDeclareKeyword) { - if ( - node.initializer && - (['let', 'var'].includes(kind) || node.type) - ) { - this.#throwError( - node, - `Initializers are not permitted in ambient contexts.`, - ); - } - // Theoretically, only certain initializers are allowed for declare const, - // (TS1254: A 'const' initializer in an ambient context must be a string - // or numeric literal or literal enum reference.) but we just allow - // all expressions + if ( + hasDeclareKeyword && + node.initializer && + (['let', 'var'].includes(kind) || node.type) + ) { + this.#throwError( + node, + `Initializers are not permitted in ambient contexts.`, + ); } + // Theoretically, only certain initializers are allowed for declare const, + // (TS1254: A 'const' initializer in an ambient context must be a string + // or numeric literal or literal enum reference.) but we just allow + // all expressions + // Note! No-declare does not mean the variable is not ambient, because // it can be further nested in other declare contexts. Therefore we cannot // check for const initializers. From b98e5490c525c4990f1ce6268dedb67ba61cb4e7 Mon Sep 17 00:00:00 2001 From: fisker Date: Mon, 1 Dec 2025 22:21:42 +0800 Subject: [PATCH 07/10] Fix bad merge --- packages/typescript-estree/src/convert.ts | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index 606d46e19e34..3b2b3f216956 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -57,9 +57,9 @@ export function convertError( error: SemanticOrSyntacticError | ts.DiagnosticWithLocation, ): TSError { return createError( + error.start!, ('message' in error && error.message) || (error.messageText as string), error.file!, - error.start!, ); } @@ -166,18 +166,7 @@ export class Converter { if (this.options.allowInvalidAST) { return; } - let start; - let end; - if (Array.isArray(node)) { - [start, end] = node; - } else if (typeof node === 'number') { - start = end = node; - } else { - start = node.getStart(this.ast); - end = node.getEnd(); - } - - throw createError(message, this.ast, start, end); + throw createError(node, message, this.ast); } /** From 564f52187f860cd9b1f8efb9c9c1bfed6ceaf832 Mon Sep 17 00:00:00 2001 From: fisker Date: Mon, 1 Dec 2025 22:22:35 +0800 Subject: [PATCH 08/10] Reduce diff --- packages/typescript-estree/src/convert.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index 3b2b3f216956..1807a7c5a88a 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -166,6 +166,7 @@ export class Converter { if (this.options.allowInvalidAST) { return; } + throw createError(node, message, this.ast); } From 0f674004b30f0c9f360d19c2f4f6e3a87ac11307 Mon Sep 17 00:00:00 2001 From: fisker Date: Mon, 1 Dec 2025 22:27:48 +0800 Subject: [PATCH 09/10] Revert --- packages/typescript-estree/src/convert.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index 1807a7c5a88a..124780d89c72 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -1829,7 +1829,7 @@ export class Converter { accessibility: getTSNodeAccessibility(node), decorators: [], override: hasModifier(SyntaxKind.OverrideKeyword, node), - parameter: result, + parameter: result as TSESTree.TSParameterProperty['parameter'], readonly: hasModifier(SyntaxKind.ReadonlyKeyword, node), static: hasModifier(SyntaxKind.StaticKeyword, node), }); From f28b14af2f143705835796fad9aaaeb0bdc0845c Mon Sep 17 00:00:00 2001 From: fisker Date: Mon, 1 Dec 2025 22:33:42 +0800 Subject: [PATCH 10/10] Another --- packages/typescript-estree/src/convert.ts | 37 +++++++++++++++-------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index 124780d89c72..9e7e84dd31d7 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -3006,19 +3006,30 @@ export class Converter { }); } - const result = this.createNode(node, { - type: AST_NODE_TYPES.TSImportType, - range, - argument: this.convertChild(node.argument), - options, - qualifier: this.convertChild(node.qualifier), - typeArguments: node.typeArguments - ? this.convertTypeArgumentsToTypeParameterInstantiation( - node.typeArguments, - node, - ) - : null, - }); + const argument = this.convertChild(node.argument); + const source = argument.literal; + + const result = this.createNode( + node, + this.#withDeprecatedGetter( + { + type: AST_NODE_TYPES.TSImportType, + range, + options, + qualifier: this.convertChild(node.qualifier), + source, + typeArguments: node.typeArguments + ? this.convertTypeArgumentsToTypeParameterInstantiation( + node.typeArguments, + node, + ) + : null, + }, + 'argument', + 'source', + argument, + ), + ); if (node.isTypeOf) { return this.createNode(node, {