diff --git a/packages/eslint-plugin/src/rules/no-empty-interface.ts b/packages/eslint-plugin/src/rules/no-empty-interface.ts index 4e037e9cc8a..d74034114bb 100644 --- a/packages/eslint-plugin/src/rules/no-empty-interface.ts +++ b/packages/eslint-plugin/src/rules/no-empty-interface.ts @@ -1,4 +1,5 @@ import type { TSESLint } from '@typescript-eslint/utils'; +import { AST_NODE_TYPES } from '@typescript-eslint/utils'; import * as util from '../util'; @@ -73,29 +74,39 @@ export default util.createRule({ )}${typeParam} = ${sourceCode.getText(extend[0])}`, ); }; + const scope = context.getScope(); - // Check if interface is within ambient declaration - let useAutoFix = true; - if (util.isDefinitionFile(filename)) { - const scope = context.getScope(); - if (scope.type === 'tsModule' && scope.block.declare) { - useAutoFix = false; - } - } + const mergedWithClassDeclaration = scope.set + .get(node.id.name) + ?.defs?.some( + def => def.node.type === AST_NODE_TYPES.ClassDeclaration, + ); + + const isInAmbientDeclaration = !!( + util.isDefinitionFile(filename) && + scope.type === 'tsModule' && + scope.block.declare + ); + + const useAutoFix = !( + isInAmbientDeclaration || mergedWithClassDeclaration + ); context.report({ node: node.id, messageId: 'noEmptyWithSuper', ...(useAutoFix ? { fix } - : { + : !mergedWithClassDeclaration + ? { suggest: [ { messageId: 'noEmptyWithSuper', fix, }, ], - }), + } + : null), }); } } diff --git a/packages/eslint-plugin/tests/rules/no-empty-interface.test.ts b/packages/eslint-plugin/tests/rules/no-empty-interface.test.ts index 0d7f73342b5..893deaf01d6 100644 --- a/packages/eslint-plugin/tests/rules/no-empty-interface.test.ts +++ b/packages/eslint-plugin/tests/rules/no-empty-interface.test.ts @@ -34,6 +34,18 @@ interface Bar extends Foo {} `, options: [{ allowSingleExtends: true }], }, + { + code: ` +interface Foo { + props: string; +} + +interface Bar extends Foo {} + +class Bar {} + `, + options: [{ allowSingleExtends: true }], + }, ], invalid: [ { @@ -58,6 +70,82 @@ interface Bar extends Foo {} }, { code: ` +interface Foo { + props: string; +} + +interface Bar extends Foo {} + +class Baz {} + `, + output: ` +interface Foo { + props: string; +} + +type Bar = Foo + +class Baz {} + `, + options: [{ allowSingleExtends: false }], + errors: [ + { + messageId: 'noEmptyWithSuper', + line: 6, + column: 11, + }, + ], + }, + { + code: ` +interface Foo { + props: string; +} + +interface Bar extends Foo {} + +class Bar {} + `, + options: [{ allowSingleExtends: false }], + errors: [ + { + messageId: 'noEmptyWithSuper', + line: 6, + column: 11, + }, + ], + output: null, + }, + { + code: ` +interface Foo { + props: string; +} + +interface Bar extends Foo {} + +const bar = class Bar {}; + `, + output: ` +interface Foo { + props: string; +} + +type Bar = Foo + +const bar = class Bar {}; + `, + options: [{ allowSingleExtends: false }], + errors: [ + { + messageId: 'noEmptyWithSuper', + line: 6, + column: 11, + }, + ], + }, + { + code: ` interface Foo { name: string; }