Skip to content

Commit 02d72d4

Browse files
authored
fix(eslint-plugin): [no-unused-vars] better handling for declared modules (typescript-eslint#2553)
Fixes typescript-eslint#2523 - `declare global` should never be marked as unused. - namespaces within declared namespaces all ambiently export their children
1 parent bf88c84 commit 02d72d4

File tree

2 files changed

+97
-3
lines changed

2 files changed

+97
-3
lines changed

packages/eslint-plugin/src/rules/no-unused-vars.ts

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,19 @@ export default util.createRule<Options, MessageIds>({
219219
markDeclarationChildAsUsed(node);
220220
},
221221

222+
// global augmentation can be in any file, and they do not need exports
223+
'TSModuleDeclaration[declare = true][global = true]'(): void {
224+
context.markVariableAsUsed('global');
225+
},
226+
227+
// children of a namespace that is a child of a declared namespace are auto-exported
228+
[ambientDeclarationSelector(
229+
'TSModuleDeclaration[declare = true] > TSModuleBlock TSModuleDeclaration > TSModuleBlock',
230+
false,
231+
)](node: DeclarationSelectorNode): void {
232+
markDeclarationChildAsUsed(node);
233+
},
234+
222235
// declared namespace handling
223236
[ambientDeclarationSelector(
224237
'TSModuleDeclaration[declare = true] > TSModuleBlock',
@@ -229,9 +242,12 @@ export default util.createRule<Options, MessageIds>({
229242
util.NullThrowsReasons.MissingParent,
230243
) as TSESTree.TSModuleDeclaration;
231244

232-
// declared modules with an `export =` statement will only export that one thing
245+
// declared ambient modules with an `export =` statement will only export that one thing
233246
// all other statements are not automatically exported in this case
234-
if (checkModuleDeclForExportEquals(moduleDecl)) {
247+
if (
248+
moduleDecl.id.type === AST_NODE_TYPES.Literal &&
249+
checkModuleDeclForExportEquals(moduleDecl)
250+
) {
235251
return;
236252
}
237253

@@ -284,7 +300,7 @@ export default util.createRule<Options, MessageIds>({
284300
AST_NODE_TYPES.TSEnumDeclaration,
285301
AST_NODE_TYPES.TSModuleDeclaration,
286302
AST_NODE_TYPES.VariableDeclaration,
287-
].join(', ')})${childDeclare ? '[declare=true]' : ''}`,
303+
].join(', ')})${childDeclare ? '[declare = true]' : ''}`,
288304
].join(', ');
289305
}
290306
function markDeclarationChildAsUsed(node: DeclarationSelectorNode): void {

packages/eslint-plugin/tests/rules/no-unused-vars.test.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -858,6 +858,32 @@ declare module 'foo' {
858858
type Test = 1;
859859
const x: Test = 1;
860860
export = x;
861+
}
862+
`,
863+
// https://github.com/typescript-eslint/typescript-eslint/issues/2523
864+
`
865+
declare global {
866+
interface Foo {}
867+
}
868+
`,
869+
`
870+
declare global {
871+
namespace jest {
872+
interface Matchers<R> {
873+
toBeSeven: () => R;
874+
}
875+
}
876+
}
877+
`,
878+
`
879+
export declare namespace Foo {
880+
namespace Bar {
881+
namespace Baz {
882+
namespace Bam {
883+
const x = 1;
884+
}
885+
}
886+
}
861887
}
862888
`,
863889
],
@@ -1456,5 +1482,57 @@ declare module 'foo' {
14561482
},
14571483
],
14581484
},
1485+
{
1486+
code: `
1487+
// not declared
1488+
export namespace Foo {
1489+
namespace Bar {
1490+
namespace Baz {
1491+
namespace Bam {
1492+
const x = 1;
1493+
}
1494+
}
1495+
}
1496+
}
1497+
`,
1498+
errors: [
1499+
{
1500+
messageId: 'unusedVar',
1501+
line: 4,
1502+
data: {
1503+
varName: 'Bar',
1504+
action: 'defined',
1505+
additional: '',
1506+
},
1507+
},
1508+
{
1509+
messageId: 'unusedVar',
1510+
line: 5,
1511+
data: {
1512+
varName: 'Baz',
1513+
action: 'defined',
1514+
additional: '',
1515+
},
1516+
},
1517+
{
1518+
messageId: 'unusedVar',
1519+
line: 6,
1520+
data: {
1521+
varName: 'Bam',
1522+
action: 'defined',
1523+
additional: '',
1524+
},
1525+
},
1526+
{
1527+
messageId: 'unusedVar',
1528+
line: 7,
1529+
data: {
1530+
varName: 'x',
1531+
action: 'assigned a value',
1532+
additional: '',
1533+
},
1534+
},
1535+
],
1536+
},
14591537
],
14601538
});

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy