diff --git a/packages/eslint-plugin/docs/rules/no-empty-interface.md b/packages/eslint-plugin/docs/rules/no-empty-interface.md index a300c81b1041..37aa7e4dbc11 100644 --- a/packages/eslint-plugin/docs/rules/no-empty-interface.md +++ b/packages/eslint-plugin/docs/rules/no-empty-interface.md @@ -38,6 +38,23 @@ interface Bar { interface Baz extends Foo, Bar {} ``` +### Options + +This rule accepts a single object option with the following default configuration: + +```json +{ + "@typescript-eslint/no-empty-interface": [ + "error", + { + "allowSingleExtends": false + } + ] +} +``` + +- `allowSingleExtends: true` will silence warnings about extending a single interface without adding additional members + ## When Not To Use It If you don't care about having empty/meaningless interfaces, then you will not need this rule. diff --git a/packages/eslint-plugin/src/rules/no-empty-interface.ts b/packages/eslint-plugin/src/rules/no-empty-interface.ts index 52e1d0ddf9ab..f4373a89375e 100644 --- a/packages/eslint-plugin/src/rules/no-empty-interface.ts +++ b/packages/eslint-plugin/src/rules/no-empty-interface.ts @@ -6,7 +6,14 @@ import { TSESTree } from '@typescript-eslint/typescript-estree'; import * as util from '../util'; -export default util.createRule({ +type Options = [ + { + allowSingleExtends?: boolean; + } +]; +type MessageIds = 'noEmpty' | 'noEmptyWithSuper'; + +export default util.createRule({ name: 'no-empty-interface', meta: { type: 'suggestion', @@ -21,13 +28,28 @@ export default util.createRule({ noEmptyWithSuper: 'An interface declaring no members is equivalent to its supertype.' }, - schema: [] + schema: [ + { + type: 'object', + additionalProperties: false, + properties: { + allowSingleExtends: { + type: 'boolean' + } + } + } + ] }, - defaultOptions: [], - create(context) { + defaultOptions: [ + { + allowSingleExtends: false + } + ], + create(context, [{ allowSingleExtends }]) { return { TSInterfaceDeclaration(node: TSESTree.TSInterfaceDeclaration) { if (node.body.body.length !== 0) { + // interface contains members --> Nothing to report return; } @@ -37,10 +59,15 @@ export default util.createRule({ messageId: 'noEmpty' }); } else if (node.extends.length === 1) { - context.report({ - node: node.id, - messageId: 'noEmptyWithSuper' - }); + // interface extends exactly 1 interface --> Report depending on rule setting + if (allowSingleExtends) { + return; + } else { + context.report({ + node: node.id, + messageId: 'noEmptyWithSuper' + }); + } } } }; 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 2410509a1fcd..fb7b95009129 100644 --- a/packages/eslint-plugin/tests/rules/no-empty-interface.test.ts +++ b/packages/eslint-plugin/tests/rules/no-empty-interface.test.ts @@ -23,7 +23,17 @@ interface Bar { // valid because extending multiple interfaces can be used instead of a union type interface Baz extends Foo, Bar {} - ` + `, + { + code: ` +interface Foo { + name: string; +} + +interface Bar extends Foo {} + `, + options: [{ allowSingleExtends: true }] + } ], invalid: [ { @@ -54,6 +64,7 @@ interface Foo { interface Bar extends Foo {} `, + options: [{ allowSingleExtends: false }], errors: [ { messageId: 'noEmptyWithSuper',