Skip to content

Commit

Permalink
fix(eslint-plugin): [no-unused-vars] better handling for declared mod…
Browse files Browse the repository at this point in the history
…ules (#2553)

Fixes #2523

- `declare global` should never be marked as unused.
- namespaces within declared namespaces all ambiently export their children
  • Loading branch information
bradzacher committed Sep 13, 2020
1 parent bf88c84 commit 02d72d4
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 3 deletions.
22 changes: 19 additions & 3 deletions packages/eslint-plugin/src/rules/no-unused-vars.ts
Expand Up @@ -219,6 +219,19 @@ export default util.createRule<Options, MessageIds>({
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',
Expand All @@ -229,9 +242,12 @@ export default util.createRule<Options, MessageIds>({
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;
}

Expand Down Expand Up @@ -284,7 +300,7 @@ export default util.createRule<Options, MessageIds>({
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 {
Expand Down
78 changes: 78 additions & 0 deletions packages/eslint-plugin/tests/rules/no-unused-vars.test.ts
Expand Up @@ -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<R> {
toBeSeven: () => R;
}
}
}
`,
`
export declare namespace Foo {
namespace Bar {
namespace Baz {
namespace Bam {
const x = 1;
}
}
}
}
`,
],
Expand Down Expand Up @@ -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: '',
},
},
],
},
],
});

0 comments on commit 02d72d4

Please sign in to comment.