diff --git a/packages/typescript-estree/src/check-syntax-errors.ts b/packages/typescript-estree/src/check-syntax-errors.ts new file mode 100644 index 000000000000..1eb780abcc7d --- /dev/null +++ b/packages/typescript-estree/src/check-syntax-errors.ts @@ -0,0 +1,70 @@ +import * as ts from 'typescript'; + +import type { TSNode } from './ts-estree'; + +import { checkModifiers } from './check-modifiers'; +import { isValidAssignmentTarget, createError } from './node-utils'; + +const SyntaxKind = ts.SyntaxKind; + +export function checkSyntaxError(tsNode: ts.Node): void { + checkModifiers(tsNode); + + const node = tsNode as TSNode; + + switch (node.kind) { + case SyntaxKind.ForInStatement: + case SyntaxKind.ForOfStatement: { + checkForStatementDeclaration(node); + break; + } + default: { + break; + } + } +} + +function checkForStatementDeclaration( + node: ts.ForInStatement | ts.ForOfStatement, +): void { + const { initializer, kind } = node; + const loop = kind === SyntaxKind.ForInStatement ? 'for...in' : 'for...of'; + if (ts.isVariableDeclarationList(initializer)) { + if (initializer.declarations.length !== 1) { + throw createError( + initializer, + `Only a single variable declaration is allowed in a '${loop}' statement.`, + ); + } + const declaration = initializer.declarations[0]; + if (declaration.initializer) { + throw createError( + declaration, + `The variable declaration of a '${loop}' statement cannot have an initializer.`, + ); + } else if (declaration.type) { + throw createError( + declaration, + `The variable declaration of a '${loop}' statement cannot have a type annotation.`, + ); + } + if ( + kind === SyntaxKind.ForInStatement && + initializer.flags & ts.NodeFlags.Using + ) { + throw createError( + initializer, + "The left-hand side of a 'for...in' statement cannot be a 'using' declaration.", + ); + } + } else if ( + !isValidAssignmentTarget(initializer) && + initializer.kind !== SyntaxKind.ObjectLiteralExpression && + initializer.kind !== SyntaxKind.ArrayLiteralExpression + ) { + throw createError( + initializer, + `The left-hand side of a '${loop}' statement must be a variable or a property access.`, + ); + } +} diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index 952b6d30756a..344b4b175efd 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -10,7 +10,7 @@ import type { import type { SemanticOrSyntacticError } from './semantic-or-syntactic-errors'; import type { TSESTree, TSESTreeToTSNode, TSNode } from './ts-estree'; -import { checkModifiers } from './check-modifiers'; +import { checkSyntaxError } from './check-syntax-errors'; import { getDecorators, getModifiers } from './getModifiers'; import { canContainDirective, @@ -105,58 +105,12 @@ export class Converter { this.options = { ...options }; } - #checkForStatementDeclaration( - initializer: ts.ForInitializer, - kind: ts.SyntaxKind.ForInStatement | ts.SyntaxKind.ForOfStatement, - ): void { - const loop = - kind === ts.SyntaxKind.ForInStatement ? 'for...in' : 'for...of'; - if (ts.isVariableDeclarationList(initializer)) { - if (initializer.declarations.length !== 1) { - this.#throwError( - initializer, - `Only a single variable declaration is allowed in a '${loop}' statement.`, - ); - } - const declaration = initializer.declarations[0]; - if (declaration.initializer) { - this.#throwError( - declaration, - `The variable declaration of a '${loop}' statement cannot have an initializer.`, - ); - } else if (declaration.type) { - this.#throwError( - declaration, - `The variable declaration of a '${loop}' statement cannot have a type annotation.`, - ); - } - if ( - kind === ts.SyntaxKind.ForInStatement && - initializer.flags & ts.NodeFlags.Using - ) { - this.#throwError( - initializer, - "The left-hand side of a 'for...in' statement cannot be a 'using' declaration.", - ); - } - } else if ( - !isValidAssignmentTarget(initializer) && - initializer.kind !== ts.SyntaxKind.ObjectLiteralExpression && - initializer.kind !== ts.SyntaxKind.ArrayLiteralExpression - ) { - this.#throwError( - initializer, - `The left-hand side of a '${loop}' statement must be a variable or a property access.`, - ); - } - } - - #checkModifiers(node: ts.Node): void { + #checkSyntaxError(node: ts.Node): void { if (this.options.allowInvalidAST) { return; } - checkModifiers(node); + checkSyntaxError(node); } #throwError( @@ -528,7 +482,7 @@ export class Converter { return null; } - this.#checkModifiers(node); + this.#checkSyntaxError(node); const pattern = this.allowPattern; if (allowPattern != null) { @@ -896,7 +850,6 @@ export class Converter { }); case SyntaxKind.ForInStatement: - this.#checkForStatementDeclaration(node.initializer, node.kind); return this.createNode(node, { type: AST_NODE_TYPES.ForInStatement, body: this.convertChild(node.statement), @@ -905,7 +858,6 @@ export class Converter { }); case SyntaxKind.ForOfStatement: { - this.#checkForStatementDeclaration(node.initializer, node.kind); return this.createNode(node, { type: AST_NODE_TYPES.ForOfStatement, await: Boolean(