diff --git a/packages/eslint-plugin/src/rules/no-unused-vars.ts b/packages/eslint-plugin/src/rules/no-unused-vars.ts index 03c27d40616..7239982c935 100644 --- a/packages/eslint-plugin/src/rules/no-unused-vars.ts +++ b/packages/eslint-plugin/src/rules/no-unused-vars.ts @@ -219,6 +219,19 @@ export default util.createRule({ markDeclarationChildAsUsed(node); }, + // global augmentation can be in any file, and they do not need exports + 'TSModuleDeclaration[declare = true][global = true]'(): void { + context.markVariableAsUsed('global'); + }, + + // children of a namespace that is a child of a declared namespace are auto-exported + [ambientDeclarationSelector( + 'TSModuleDeclaration[declare = true] > TSModuleBlock TSModuleDeclaration > TSModuleBlock', + false, + )](node: DeclarationSelectorNode): void { + markDeclarationChildAsUsed(node); + }, + // declared namespace handling [ambientDeclarationSelector( 'TSModuleDeclaration[declare = true] > TSModuleBlock', @@ -229,9 +242,12 @@ export default util.createRule({ util.NullThrowsReasons.MissingParent, ) as TSESTree.TSModuleDeclaration; - // declared modules with an `export =` statement will only export that one thing + // declared ambient modules with an `export =` statement will only export that one thing // all other statements are not automatically exported in this case - if (checkModuleDeclForExportEquals(moduleDecl)) { + if ( + moduleDecl.id.type === AST_NODE_TYPES.Literal && + checkModuleDeclForExportEquals(moduleDecl) + ) { return; } @@ -284,7 +300,7 @@ export default util.createRule({ AST_NODE_TYPES.TSEnumDeclaration, AST_NODE_TYPES.TSModuleDeclaration, AST_NODE_TYPES.VariableDeclaration, - ].join(', ')})${childDeclare ? '[declare=true]' : ''}`, + ].join(', ')})${childDeclare ? '[declare = true]' : ''}`, ].join(', '); } function markDeclarationChildAsUsed(node: DeclarationSelectorNode): void { diff --git a/packages/eslint-plugin/tests/rules/no-unused-vars.test.ts b/packages/eslint-plugin/tests/rules/no-unused-vars.test.ts index fbb468e375f..e22f9eb2ba7 100644 --- a/packages/eslint-plugin/tests/rules/no-unused-vars.test.ts +++ b/packages/eslint-plugin/tests/rules/no-unused-vars.test.ts @@ -858,6 +858,32 @@ declare module 'foo' { type Test = 1; const x: Test = 1; export = x; +} + `, + // https://github.com/typescript-eslint/typescript-eslint/issues/2523 + ` +declare global { + interface Foo {} +} + `, + ` +declare global { + namespace jest { + interface Matchers { + toBeSeven: () => R; + } + } +} + `, + ` +export declare namespace Foo { + namespace Bar { + namespace Baz { + namespace Bam { + const x = 1; + } + } + } } `, ], @@ -1456,5 +1482,57 @@ declare module 'foo' { }, ], }, + { + code: ` +// not declared +export namespace Foo { + namespace Bar { + namespace Baz { + namespace Bam { + const x = 1; + } + } + } +} + `, + errors: [ + { + messageId: 'unusedVar', + line: 4, + data: { + varName: 'Bar', + action: 'defined', + additional: '', + }, + }, + { + messageId: 'unusedVar', + line: 5, + data: { + varName: 'Baz', + action: 'defined', + additional: '', + }, + }, + { + messageId: 'unusedVar', + line: 6, + data: { + varName: 'Bam', + action: 'defined', + additional: '', + }, + }, + { + messageId: 'unusedVar', + line: 7, + data: { + varName: 'x', + action: 'assigned a value', + additional: '', + }, + }, + ], + }, ], });