Skip to content

Commit c71f423

Browse files
authored
fix(eslint-plugin): [no-duplicate-imports] distinguish member, default (#2637)
1 parent daac9da commit c71f423

File tree

2 files changed

+57
-5
lines changed

2 files changed

+57
-5
lines changed

packages/eslint-plugin/src/rules/no-duplicate-imports.ts

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ export default util.createRule<Options, MessageIds>({
3535
create(context, [option]) {
3636
const rules = baseRule.create(context);
3737
const includeExports = option.includeExports;
38-
const typeImports = new Set();
38+
const typeMemberImports = new Set();
39+
const typeDefaultImports = new Set();
3940
const typeExports = new Set();
4041

4142
function report(
@@ -62,16 +63,32 @@ export default util.createRule<Options, MessageIds>({
6263
);
6364
}
6465

66+
function isAllMemberImport(node: TSESTree.ImportDeclaration): boolean {
67+
return node.specifiers.every(
68+
specifier => specifier.type === AST_NODE_TYPES.ImportSpecifier,
69+
);
70+
}
71+
6572
function checkTypeImport(node: TSESTree.ImportDeclaration): void {
6673
if (isStringLiteral(node.source)) {
6774
const value = node.source.value;
68-
if (typeImports.has(value)) {
75+
const isMemberImport = isAllMemberImport(node);
76+
if (
77+
isMemberImport
78+
? typeMemberImports.has(value)
79+
: typeDefaultImports.has(value)
80+
) {
6981
report('importType', node, value);
7082
}
83+
7184
if (includeExports && typeExports.has(value)) {
7285
report('importTypeAs', node, value);
7386
}
74-
typeImports.add(value);
87+
if (isMemberImport) {
88+
typeMemberImports.add(value);
89+
} else {
90+
typeDefaultImports.add(value);
91+
}
7592
}
7693
}
7794

@@ -83,7 +100,7 @@ export default util.createRule<Options, MessageIds>({
83100
if (typeExports.has(value)) {
84101
report('exportType', node, value);
85102
}
86-
if (typeImports.has(value)) {
103+
if (typeMemberImports.has(value) || typeDefaultImports.has(value)) {
87104
report('exportTypeAs', node, value);
88105
}
89106
typeExports.add(value);

packages/eslint-plugin/tests/rules/no-duplicate-imports.test.ts

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,32 @@ const ruleTester = new RuleTester({
55
parser: '@typescript-eslint/parser',
66
});
77

8-
ruleTester.run('no-dupe-class-members', rule, {
8+
ruleTester.run('no-duplicate-imports', rule, {
99
valid: [
1010
{
1111
code: "import type foo from 'foo';",
1212
},
1313
{
1414
code: "import type { foo } from 'foo';",
1515
},
16+
{
17+
code: `
18+
import type { foo } from 'foo';
19+
import type Bar from 'foo';
20+
`,
21+
},
22+
{
23+
code: `
24+
import type Foo from 'foo';
25+
import type { bar } from 'foo';
26+
`,
27+
},
28+
{
29+
code: `
30+
import type Foo from 'foo';
31+
import type { bar as Bar } from 'foo';
32+
`,
33+
},
1634
{
1735
code: `
1836
import foo from 'foo';
@@ -69,6 +87,14 @@ ruleTester.run('no-dupe-class-members', rule, {
6987
`,
7088
options: [{ includeExports: true }],
7189
},
90+
{
91+
code: `
92+
import type Foo from 'foo';
93+
import type { bar } from 'foo';
94+
export type { bar };
95+
`,
96+
options: [{ includeExports: true }],
97+
},
7298
],
7399
invalid: [
74100
{
@@ -116,6 +142,15 @@ ruleTester.run('no-dupe-class-members', rule, {
116142
options: [{ includeExports: true }],
117143
errors: [{ messageId: 'exportTypeAs' }],
118144
},
145+
{
146+
code: `
147+
import type Foo from 'foo';
148+
import type { bar } from 'foo';
149+
export type { bar } from 'foo';
150+
`,
151+
options: [{ includeExports: true }],
152+
errors: [{ messageId: 'exportTypeAs' }],
153+
},
119154
{
120155
code: `
121156
export type * as foo from 'foo';

0 commit comments

Comments
 (0)