diff --git a/packages/ast-spec/src/declaration/TSInterfaceDeclaration/fixtures/_error_/non-identifier-extends/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/declaration/TSInterfaceDeclaration/fixtures/_error_/non-identifier-extends/snapshots/1-TSESTree-Error.shot index 3192c89d0208..70781ef9b372 100644 --- a/packages/ast-spec/src/declaration/TSInterfaceDeclaration/fixtures/_error_/non-identifier-extends/snapshots/1-TSESTree-Error.shot +++ b/packages/ast-spec/src/declaration/TSInterfaceDeclaration/fixtures/_error_/non-identifier-extends/snapshots/1-TSESTree-Error.shot @@ -1,4 +1,7 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`AST Fixtures > declaration > TSInterfaceDeclaration > _error_ > non-identifier-extends > TSESTree - Error`] -NO ERROR +TSError +> 1 | interface F extends 1 {} + | ^ Interface declaration can only extend an identifier/qualified name with optional type arguments. + 2 | diff --git a/packages/ast-spec/src/declaration/TSInterfaceDeclaration/fixtures/_error_/non-identifier-extends/snapshots/3-Alignment-Error.shot b/packages/ast-spec/src/declaration/TSInterfaceDeclaration/fixtures/_error_/non-identifier-extends/snapshots/3-Alignment-Error.shot index 8b8a05459c4b..a0844838800f 100644 --- a/packages/ast-spec/src/declaration/TSInterfaceDeclaration/fixtures/_error_/non-identifier-extends/snapshots/3-Alignment-Error.shot +++ b/packages/ast-spec/src/declaration/TSInterfaceDeclaration/fixtures/_error_/non-identifier-extends/snapshots/3-Alignment-Error.shot @@ -1,4 +1,4 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`AST Fixtures > declaration > TSInterfaceDeclaration > _error_ > non-identifier-extends > Error Alignment`] -Babel errored but TSESTree didn't +Both errored diff --git a/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-multiple-extends/snapshots/1-TSESTree-Error.shot b/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-multiple-extends/snapshots/1-TSESTree-Error.shot index b9716b289515..f57a1b692cd1 100644 --- a/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-multiple-extends/snapshots/1-TSESTree-Error.shot +++ b/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-multiple-extends/snapshots/1-TSESTree-Error.shot @@ -1,4 +1,9 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`AST Fixtures > legacy-fixtures > errorRecovery > _error_ > interface-multiple-extends > TSESTree - Error`] -NO ERROR +TSError + 1 | // TODO: This fixture might be too large, and if so should be split up. + 2 | +> 3 | interface foo extends bar extends baz {} + | ^^^^^^^^^^^ 'extends' clause already seen. + 4 | diff --git a/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-multiple-extends/snapshots/3-Alignment-Error.shot b/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-multiple-extends/snapshots/3-Alignment-Error.shot index 8c05c9ec9327..2b5ef310d7cd 100644 --- a/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-multiple-extends/snapshots/3-Alignment-Error.shot +++ b/packages/ast-spec/src/legacy-fixtures/errorRecovery/fixtures/_error_/interface-multiple-extends/snapshots/3-Alignment-Error.shot @@ -1,4 +1,4 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`AST Fixtures > legacy-fixtures > errorRecovery > _error_ > interface-multiple-extends > Error Alignment`] -Babel errored but TSESTree didn't +Both errored diff --git a/packages/ast-spec/tests/fixtures-with-differences-errors.shot b/packages/ast-spec/tests/fixtures-with-differences-errors.shot index ef7f47117298..7051aa777fbb 100644 --- a/packages/ast-spec/tests/fixtures-with-differences-errors.shot +++ b/packages/ast-spec/tests/fixtures-with-differences-errors.shot @@ -12,7 +12,6 @@ exports[`AST Fixtures > List fixtures with Error differences`] "declaration/TSImportEqualsDeclaration/fixtures/_error_/import-kind/fixture.ts", "declaration/TSInterfaceDeclaration/fixtures/_error_/missing-extends/fixture.ts", "declaration/TSInterfaceDeclaration/fixtures/_error_/missing-type-param/fixture.ts", - "declaration/TSInterfaceDeclaration/fixtures/_error_/non-identifier-extends/fixture.ts", "declaration/TSTypeAliasDeclaration/fixtures/_error_/missing-type-parameter/fixture.ts", "declaration/VariableDeclaration/fixtures/_error_/const-destructure-no-init/fixture.ts", "declaration/VariableDeclaration/fixtures/_error_/const-destructure-type-no-init/fixture.ts", @@ -45,7 +44,6 @@ exports[`AST Fixtures > List fixtures with Error differences`] "legacy-fixtures/errorRecovery/fixtures/_error_/empty-type-parameters/fixture.ts", "legacy-fixtures/errorRecovery/fixtures/_error_/index-signature-parameters/fixture.ts", "legacy-fixtures/errorRecovery/fixtures/_error_/interface-empty-extends/fixture.ts", - "legacy-fixtures/errorRecovery/fixtures/_error_/interface-multiple-extends/fixture.ts", "legacy-fixtures/errorRecovery/fixtures/_error_/interface-with-optional-index-signature/fixture.ts", "legacy-fixtures/parameter-decorators/fixtures/_error_/parameter-array-pattern-decorator/fixture.ts", "legacy-fixtures/parameter-decorators/fixtures/_error_/parameter-rest-element-decorator/fixture.ts", diff --git a/packages/eslint-plugin/tests/rules/no-unused-vars/no-unused-vars.test.ts b/packages/eslint-plugin/tests/rules/no-unused-vars/no-unused-vars.test.ts index 5ebc8d9a406b..5f40bbcdc3cc 100644 --- a/packages/eslint-plugin/tests/rules/no-unused-vars/no-unused-vars.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unused-vars/no-unused-vars.test.ts @@ -360,25 +360,6 @@ export interface Bar extends baz.test {} code: ` import test from 'test'; import baz from 'baz'; -export interface Bar extends baz().test {} - `, - errors: [ - { - column: 8, - data: { - action: 'defined', - additional: '', - varName: 'test', - }, - line: 2, - messageId: 'unusedVar', - }, - ], - }, - { - code: ` -import test from 'test'; -import baz from 'baz'; export class Bar implements baz.test {} `, errors: [ diff --git a/packages/typescript-estree/src/convert.ts b/packages/typescript-estree/src/convert.ts index 22c0257505a2..8f293db2af02 100644 --- a/packages/typescript-estree/src/convert.ts +++ b/packages/typescript-estree/src/convert.ts @@ -70,6 +70,25 @@ export interface ASTMaps { tsNodeToESTreeNodeMap: ParserWeakMap; } +function isPropertyAccessEntityNameExpression( + node: ts.Node, +): node is ts.PropertyAccessEntityNameExpression { + return ( + ts.isPropertyAccessExpression(node) && + ts.isIdentifier(node.name) && + isEntityNameExpression(node.expression) + ); +} + +function isEntityNameExpression( + node: ts.Node, +): node is ts.EntityNameExpression { + return ( + node.kind === SyntaxKind.Identifier || + isPropertyAccessEntityNameExpression(node) + ); +} + export class Converter { private allowPattern = false; private readonly ast: ts.SourceFile; @@ -3024,6 +3043,7 @@ export class Converter { const interfaceHeritageClauses = node.heritageClauses ?? []; const interfaceExtends: TSESTree.TSInterfaceHeritage[] = []; + let seenExtendsClause = false; for (const heritageClause of interfaceHeritageClauses) { if (heritageClause.token !== SyntaxKind.ExtendsKeyword) { this.#throwError( @@ -3033,8 +3053,21 @@ export class Converter { : 'Unexpected token.', ); } + if (seenExtendsClause) { + this.#throwError(heritageClause, "'extends' clause already seen."); + } + seenExtendsClause = true; for (const heritageType of heritageClause.types) { + if ( + !isEntityNameExpression(heritageType.expression) || + ts.isOptionalChain(heritageType.expression) + ) { + this.#throwError( + heritageType, + 'Interface declaration can only extend an identifier/qualified name with optional type arguments.', + ); + } interfaceExtends.push( this.convertChild( heritageType,