diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d583358ce..5a8e99b5e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). This change log adheres to standards from [Keep a CHANGELOG](http://keepachangelog.com). ## [Unreleased] +### Fixed +- [`no-unused-modules`]: consider exported TypeScript interfaces, types and enums ([#1680], thanks [@nicolashenry]) ## [2.21.2] - 2020-06-09 ### Fixed diff --git a/src/rules/no-unused-modules.js b/src/rules/no-unused-modules.js index 25139b681f..b2415c3187 100644 --- a/src/rules/no-unused-modules.js +++ b/src/rules/no-unused-modules.js @@ -63,6 +63,11 @@ const IMPORT_DEFAULT_SPECIFIER = 'ImportDefaultSpecifier' const VARIABLE_DECLARATION = 'VariableDeclaration' const FUNCTION_DECLARATION = 'FunctionDeclaration' const CLASS_DECLARATION = 'ClassDeclaration' +const INTERFACE_DECLARATION = 'InterfaceDeclaration' +const TYPE_ALIAS = 'TypeAlias' +const TS_INTERFACE_DECLARATION = 'TSInterfaceDeclaration' +const TS_TYPE_ALIAS_DECLARATION = 'TSTypeAliasDeclaration' +const TS_ENUM_DECLARATION = 'TSEnumDeclaration' const DEFAULT = 'default' /** @@ -562,7 +567,12 @@ module.exports = { if (declaration) { if ( declaration.type === FUNCTION_DECLARATION || - declaration.type === CLASS_DECLARATION + declaration.type === CLASS_DECLARATION || + declaration.type === INTERFACE_DECLARATION || + declaration.type === TYPE_ALIAS || + declaration.type === TS_INTERFACE_DECLARATION || + declaration.type === TS_TYPE_ALIAS_DECLARATION || + declaration.type === TS_ENUM_DECLARATION ) { newExportIdentifiers.add(declaration.id.name) } @@ -886,7 +896,12 @@ module.exports = { if (node.declaration) { if ( node.declaration.type === FUNCTION_DECLARATION || - node.declaration.type === CLASS_DECLARATION + node.declaration.type === CLASS_DECLARATION || + node.declaration.type === INTERFACE_DECLARATION || + node.declaration.type === TYPE_ALIAS || + node.declaration.type === TS_INTERFACE_DECLARATION || + node.declaration.type === TS_TYPE_ALIAS_DECLARATION || + node.declaration.type === TS_ENUM_DECLARATION ) { checkUsage(node, node.declaration.id.name) } diff --git a/tests/files/no-unused-modules/typescript/file-ts-a.ts b/tests/files/no-unused-modules/typescript/file-ts-a.ts index a4272256e6..a5cc566715 100644 --- a/tests/files/no-unused-modules/typescript/file-ts-a.ts +++ b/tests/files/no-unused-modules/typescript/file-ts-a.ts @@ -1,3 +1,8 @@ import {b} from './file-ts-b'; +import {c} from './file-ts-c'; +import {d} from './file-ts-d'; +import {e} from './file-ts-e'; -export const a = b + 1; +export const a = b + 1 + e.f; +export const a2: c = {}; +export const a3: d = {}; diff --git a/tests/files/no-unused-modules/typescript/file-ts-c.ts b/tests/files/no-unused-modules/typescript/file-ts-c.ts new file mode 100644 index 0000000000..aedf4062be --- /dev/null +++ b/tests/files/no-unused-modules/typescript/file-ts-c.ts @@ -0,0 +1 @@ +export interface c {}; diff --git a/tests/files/no-unused-modules/typescript/file-ts-d.ts b/tests/files/no-unused-modules/typescript/file-ts-d.ts new file mode 100644 index 0000000000..7679b3de03 --- /dev/null +++ b/tests/files/no-unused-modules/typescript/file-ts-d.ts @@ -0,0 +1 @@ +export type d = {}; diff --git a/tests/files/no-unused-modules/typescript/file-ts-e.ts b/tests/files/no-unused-modules/typescript/file-ts-e.ts new file mode 100644 index 0000000000..d1787a11af --- /dev/null +++ b/tests/files/no-unused-modules/typescript/file-ts-e.ts @@ -0,0 +1 @@ +export enum e { f }; diff --git a/tests/src/rules/no-unused-modules.js b/tests/src/rules/no-unused-modules.js index ef2d3e66c2..74200fb0d9 100644 --- a/tests/src/rules/no-unused-modules.js +++ b/tests/src/rules/no-unused-modules.js @@ -1,4 +1,4 @@ -import { test, testFilePath } from '../utils' +import { test, testFilePath, getTSParsers } from '../utils' import jsxConfig from '../../../config/react' import typescriptConfig from '../../../config/typescript' @@ -736,10 +736,81 @@ describe('correctly work with Typescript only files', () => { error(`exported declaration 'b' not used within other modules`), ], }), + test({ + options: unusedExportsTypescriptOptions, + code: `export interface c {};`, + parser: require.resolve('babel-eslint'), + filename: testFilePath('./no-unused-modules/typescript/file-ts-c.ts'), + errors: [ + error(`exported declaration 'c' not used within other modules`), + ], + }), + test({ + options: unusedExportsTypescriptOptions, + code: `export type d = {};`, + parser: require.resolve('babel-eslint'), + filename: testFilePath('./no-unused-modules/typescript/file-ts-d.ts'), + errors: [ + error(`exported declaration 'd' not used within other modules`), + ], + }), ], }) }) +context('TypeScript', function () { + getTSParsers().forEach((parser) => { + typescriptRuleTester.run('no-unused-modules', rule, { + valid: [ + test({ + options: unusedExportsTypescriptOptions, + code: 'import a from "file-ts-a";', + parser: parser, + filename: testFilePath('./no-unused-modules/typescript/file-ts-a.ts'), + }), + ], + invalid: [ + test({ + options: unusedExportsTypescriptOptions, + code: `export const b = 2;`, + parser: parser, + filename: testFilePath('./no-unused-modules/typescript/file-ts-b.ts'), + errors: [ + error(`exported declaration 'b' not used within other modules`), + ], + }), + test({ + options: unusedExportsTypescriptOptions, + code: `export interface c {};`, + parser: parser, + filename: testFilePath('./no-unused-modules/typescript/file-ts-c.ts'), + errors: [ + error(`exported declaration 'c' not used within other modules`), + ], + }), + test({ + options: unusedExportsTypescriptOptions, + code: `export type d = {};`, + parser: parser, + filename: testFilePath('./no-unused-modules/typescript/file-ts-d.ts'), + errors: [ + error(`exported declaration 'd' not used within other modules`), + ], + }), + test({ + options: unusedExportsTypescriptOptions, + code: `export enum e { f };`, + parser: parser, + filename: testFilePath('./no-unused-modules/typescript/file-ts-e.ts'), + errors: [ + error(`exported declaration 'e' not used within other modules`), + ], + }), + ], + }) + }) +}) + describe('correctly work with JSX only files', () => { jsxRuleTester.run('no-unused-modules', rule, { valid: [