Skip to content

Commit

Permalink
fix(eslint-plugin): [consistent-type-definitions] remove fixer when t…
Browse files Browse the repository at this point in the history
…he interface is within a global module declaration (#2739)
  • Loading branch information
Daniil Dubrava committed Nov 14, 2020
1 parent 9441d50 commit 2326238
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 23 deletions.
71 changes: 48 additions & 23 deletions packages/eslint-plugin/src/rules/consistent-type-definitions.ts
@@ -1,4 +1,5 @@
import {
AST_NODE_TYPES,
AST_TOKEN_TYPES,
TSESLint,
TSESTree,
Expand Down Expand Up @@ -31,6 +32,21 @@ export default util.createRule({
create(context, [option]) {
const sourceCode = context.getSourceCode();

/**
* Iterates from the highest parent to the currently traversed node
* to determine whether any node in tree is globally declared module declaration
*/
function isCurrentlyTraversedNodeWithinModuleDeclaration(): boolean {
return context
.getAncestors()
.some(
node =>
node.type === AST_NODE_TYPES.TSModuleDeclaration &&
node.declare &&
node.global,
);
}

return {
"TSTypeAliasDeclaration[typeAnnotation.type='TSTypeLiteral']"(
node: TSESTree.TSTypeAliasDeclaration,
Expand Down Expand Up @@ -73,32 +89,41 @@ export default util.createRule({
context.report({
node: node.id,
messageId: 'typeOverInterface',
fix(fixer) {
const typeNode = node.typeParameters ?? node.id;
const fixes: TSESLint.RuleFix[] = [];
/**
* remove automatically fix when the interface is within a declare global
* @see {@link https://github.com/typescript-eslint/typescript-eslint/issues/2707}
*/
fix: isCurrentlyTraversedNodeWithinModuleDeclaration()
? null
: (fixer): TSESLint.RuleFix[] => {
const typeNode = node.typeParameters ?? node.id;
const fixes: TSESLint.RuleFix[] = [];

const firstToken = sourceCode.getFirstToken(node);
if (firstToken) {
fixes.push(fixer.replaceText(firstToken, 'type'));
fixes.push(
fixer.replaceTextRange(
[typeNode.range[1], node.body.range[0]],
' = ',
),
);
}
const firstToken = sourceCode.getFirstToken(node);
if (firstToken) {
fixes.push(fixer.replaceText(firstToken, 'type'));
fixes.push(
fixer.replaceTextRange(
[typeNode.range[1], node.body.range[0]],
' = ',
),
);
}

if (node.extends) {
node.extends.forEach(heritage => {
const typeIdentifier = sourceCode.getText(heritage);
fixes.push(
fixer.insertTextAfter(node.body, ` & ${typeIdentifier}`),
);
});
}
if (node.extends) {
node.extends.forEach(heritage => {
const typeIdentifier = sourceCode.getText(heritage);
fixes.push(
fixer.insertTextAfter(
node.body,
` & ${typeIdentifier}`,
),
);
});
}

return fixes;
},
return fixes;
},
});
}
},
Expand Down
Expand Up @@ -197,5 +197,89 @@ export type W<T> = {
},
],
},
{
code: `
namespace JSX {
interface Array<T> {
foo(x: (x: number) => T): T[];
}
}
`,
output: noFormat`
namespace JSX {
type Array<T> = {
foo(x: (x: number) => T): T[];
}
}
`,
options: ['type'],
errors: [
{
messageId: 'typeOverInterface',
line: 3,
column: 13,
},
],
},
{
code: `
global {
interface Array<T> {
foo(x: (x: number) => T): T[];
}
}
`,
output: noFormat`
global {
type Array<T> = {
foo(x: (x: number) => T): T[];
}
}
`,
options: ['type'],
errors: [
{
messageId: 'typeOverInterface',
line: 3,
column: 13,
},
],
},
{
code: `
declare global {
interface Array<T> {
foo(x: (x: number) => T): T[];
}
}
`,
output: null,
options: ['type'],
errors: [
{
messageId: 'typeOverInterface',
line: 3,
column: 13,
},
],
},
{
code: `
declare global {
namespace Foo {
interface Bar {}
}
}
`,
output: null,
options: ['type'],
errors: [
{
messageId: 'typeOverInterface',
line: 4,
column: 15,
},
],
},
],
});

0 comments on commit 2326238

Please sign in to comment.