From 62b2278aec0011c93eae17bed8b278114d3379a2 Mon Sep 17 00:00:00 2001 From: G r e y Date: Sun, 12 Apr 2020 00:07:49 -0500 Subject: [PATCH] fix(eslint-plugin): [no-empty-interface] use suggestion fixer for ambient contexts (#1880) --- .../src/rules/no-empty-interface.ts | 54 ++++++++++++++----- .../tests/rules/no-empty-interface.test.ts | 36 +++++++++++++ 2 files changed, 78 insertions(+), 12 deletions(-) diff --git a/packages/eslint-plugin/src/rules/no-empty-interface.ts b/packages/eslint-plugin/src/rules/no-empty-interface.ts index 2e9e61c1e0e..d279e9bd4f6 100644 --- a/packages/eslint-plugin/src/rules/no-empty-interface.ts +++ b/packages/eslint-plugin/src/rules/no-empty-interface.ts @@ -1,4 +1,8 @@ import * as util from '../util'; +import { + AST_NODE_TYPES, + TSESLint, +} from '@typescript-eslint/experimental-utils'; type Options = [ { @@ -43,6 +47,7 @@ export default util.createRule({ return { TSInterfaceDeclaration(node): void { const sourceCode = context.getSourceCode(); + const filename = context.getFilename(); if (node.body.body.length !== 0) { // interface contains members --> Nothing to report @@ -58,21 +63,46 @@ export default util.createRule({ } else if (extend.length === 1) { // interface extends exactly 1 interface --> Report depending on rule setting if (!allowSingleExtends) { + const fix = (fixer: TSESLint.RuleFixer): TSESLint.RuleFix => { + let typeParam = ''; + if (node.typeParameters) { + typeParam = sourceCode.getText(node.typeParameters); + } + return fixer.replaceText( + node, + `type ${sourceCode.getText( + node.id, + )}${typeParam} = ${sourceCode.getText(extend[0])}`, + ); + }; + + // Check if interface is within ambient declaration + let useAutoFix = true; + if (util.isDefinitionFile(filename)) { + const scope = context.getScope(); + if ( + scope.block.parent && + scope.block.parent.type === + AST_NODE_TYPES.TSModuleDeclaration && + scope.block.parent.declare + ) { + useAutoFix = false; + } + } + context.report({ node: node.id, messageId: 'noEmptyWithSuper', - fix: fixer => { - let typeParam = ''; - if (node.typeParameters) { - typeParam = sourceCode.getText(node.typeParameters); - } - return fixer.replaceText( - node, - `type ${sourceCode.getText( - node.id, - )}${typeParam} = ${sourceCode.getText(extend[0])}`, - ); - }, + ...(useAutoFix + ? { fix } + : { + suggest: [ + { + messageId: 'noEmptyWithSuper', + fix, + }, + ], + }), }); } } 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 840e729f183..d0281ec77a0 100644 --- a/packages/eslint-plugin/tests/rules/no-empty-interface.test.ts +++ b/packages/eslint-plugin/tests/rules/no-empty-interface.test.ts @@ -148,5 +148,41 @@ type Foo = Bar }, ], }, + { + filename: 'test.d.ts', + code: ` +declare module FooBar { + type Baz = typeof baz; + export interface Bar extends Baz {} +} + `.trimRight(), + errors: [ + { + messageId: 'noEmptyWithSuper', + line: 4, + column: 20, + endLine: 4, + endColumn: 23, + suggestions: [ + { + messageId: 'noEmptyWithSuper', + output: noFormat` +declare module FooBar { + type Baz = typeof baz; + export type Bar = Baz +} + `.trimRight(), + }, + ], + }, + ], + // output matches input because a suggestion was made + output: ` +declare module FooBar { + type Baz = typeof baz; + export interface Bar extends Baz {} +} + `.trimRight(), + }, ], });