📄 consistent-type-imports.ts
¶
📊 Analysis Summary¶
Metric | Count |
---|---|
🔧 Functions | 10 |
📦 Imports | 14 |
📊 Variables & Constants | 30 |
📐 Interfaces | 2 |
📑 Type Aliases | 4 |
📚 Table of Contents¶
🛠️ File Location:¶
📂 packages/eslint-plugin/src/rules/consistent-type-imports.ts
📦 Imports¶
Name | Source |
---|---|
TSESLint |
@typescript-eslint/utils |
TSESTree |
@typescript-eslint/utils |
RuleListener |
@typescript-eslint/utils/eslint-utils |
AST_NODE_TYPES |
@typescript-eslint/utils |
createRule |
../util |
formatWordList |
../util |
getParserServices |
../util |
isClosingBraceToken |
../util |
isCommaToken |
../util |
isImportKeyword |
../util |
isOpeningBraceToken |
../util |
isTypeKeyword |
../util |
nullThrows |
../util |
NullThrowsReasons |
../util |
Variables & Constants¶
Name | Type | Kind | Value | Exported |
---|---|---|---|---|
prefer |
any |
const | option.prefer ?? 'type-imports' |
✗ |
disallowTypeAnnotations |
boolean |
const | option.disallowTypeAnnotations !== false |
✗ |
selectors |
RuleListener |
const | {} |
✗ |
fixStyle |
any |
const | option.fixStyle ?? 'separate-type-imports' |
✗ |
hasDecoratorMetadata |
boolean |
let/var | false |
✗ |
sourceImportsMap |
Record<string, SourceImports> |
const | {} |
✗ |
emitDecoratorMetadata |
any |
const | getParserServices(context, true).emitDecoratorMetadata ?? false |
✗ |
experimentalDecorators |
any |
const | getParserServices(context, true).experimentalDecorators ?? false |
✗ |
source |
any |
const | node.source.value |
✗ |
sourceImports |
SourceImports |
const | sourceImportsMap[source] |
✗ |
typeSpecifiers |
TSESTree.ImportClause[] |
const | [] |
✗ |
inlineTypeSpecifiers |
TSESTree.ImportSpecifier[] |
const | [] |
✗ |
valueSpecifiers |
TSESTree.ImportClause[] |
const | [] |
✗ |
unusedSpecifiers |
TSESTree.ImportClause[] |
const | [] |
✗ |
parent |
any |
let/var | ref.identifier.parent as TSESTree.Node | undefined |
✗ |
child |
TSESTree.Node |
let/var | ref.identifier |
✗ |
defaultSpecifier |
any |
const | `node.specifiers[0].type === AST_NODE_TYPES.ImportDefaultSpecifier | |
? node.specifiers[0] | ||||
: null` | ✗ | |||
namespaceSpecifier |
any |
const | `node.specifiers.find( | |
(specifier): specifier is TSESTree.ImportNamespaceSpecifier => | ||||
specifier.type === AST_NODE_TYPES.ImportNamespaceSpecifier, | ||||
) ?? null` | ✗ | |||
typeNamedSpecifiersTexts |
string[] |
const | [] |
✗ |
removeTypeNamedSpecifiers |
TSESLint.RuleFix[] |
const | [] |
✗ |
namedSpecifierGroups |
TSESTree.ImportSpecifier[][] |
const | [] |
✗ |
group |
TSESTree.ImportSpecifier[] |
let/var | [] |
✗ |
first |
TSESTree.ImportSpecifier |
const | namedSpecifierGroup[0] |
✗ |
last |
TSESTree.ImportSpecifier |
const | namedSpecifierGroup[namedSpecifierGroup.length - 1] |
✗ |
removeRange |
TSESTree.Range |
const | [first.range[0], last.range[1]] |
✗ |
textRange |
TSESTree.Range |
const | [...removeRange] |
✗ |
isFirst |
boolean |
const | allNamedSpecifiers[0] === first |
✗ |
isLast |
boolean |
const | allNamedSpecifiers[allNamedSpecifiers.length - 1] === last |
✗ |
afterFixes |
TSESLint.RuleFix[] |
let/var | [] |
✗ |
fixesRemoveTypeNamespaceSpecifier |
TSESLint.RuleFix[] |
let/var | [] |
✗ |
Functions¶
`classifySpecifier(node: TSESTree.ImportDeclaration): {¶
defaultSpecifier: TSESTree.ImportDefaultSpecifier | null;
namedSpecifiers: TSESTree.ImportSpecifier[];
namespaceSpecifier: TSESTree.ImportNamespaceSpecifier | null;
}`
Code
function classifySpecifier(node: TSESTree.ImportDeclaration): {
defaultSpecifier: TSESTree.ImportDefaultSpecifier | null;
namedSpecifiers: TSESTree.ImportSpecifier[];
namespaceSpecifier: TSESTree.ImportNamespaceSpecifier | null;
} {
const defaultSpecifier =
node.specifiers[0].type === AST_NODE_TYPES.ImportDefaultSpecifier
? node.specifiers[0]
: null;
const namespaceSpecifier =
node.specifiers.find(
(specifier): specifier is TSESTree.ImportNamespaceSpecifier =>
specifier.type === AST_NODE_TYPES.ImportNamespaceSpecifier,
) ?? null;
const namedSpecifiers = node.specifiers.filter(
(specifier): specifier is TSESTree.ImportSpecifier =>
specifier.type === AST_NODE_TYPES.ImportSpecifier,
);
return {
defaultSpecifier,
namedSpecifiers,
namespaceSpecifier,
};
}
- Parameters:
node: TSESTree.ImportDeclaration
- Return Type:
{ defaultSpecifier: TSESTree.ImportDefaultSpecifier | null; namedSpecifiers: TSESTree.ImportSpecifier[]; namespaceSpecifier: TSESTree.ImportNamespaceSpecifier | null; }
- Calls:
node.specifiers.find
node.specifiers.filter
`getFixesNamedSpecifiers(fixer: TSESLint.RuleFixer, node: TSESTree.ImportDeclaration, subsetNamedSpecifiers: TSESTree.ImportSpecifier[], allNamedSpecifiers: TSESTree.ImportSpecifier[]): {¶
removeTypeNamedSpecifiers: TSESLint.RuleFix[];
typeNamedSpecifiersText: string;
}`
Code
function getFixesNamedSpecifiers(
fixer: TSESLint.RuleFixer,
node: TSESTree.ImportDeclaration,
subsetNamedSpecifiers: TSESTree.ImportSpecifier[],
allNamedSpecifiers: TSESTree.ImportSpecifier[],
): {
removeTypeNamedSpecifiers: TSESLint.RuleFix[];
typeNamedSpecifiersText: string;
} {
if (allNamedSpecifiers.length === 0) {
return {
removeTypeNamedSpecifiers: [],
typeNamedSpecifiersText: '',
};
}
const typeNamedSpecifiersTexts: string[] = [];
const removeTypeNamedSpecifiers: TSESLint.RuleFix[] = [];
if (subsetNamedSpecifiers.length === allNamedSpecifiers.length) {
// import Foo, {Type1, Type2} from 'foo'
// import DefType, {Type1, Type2} from 'foo'
const openingBraceToken = nullThrows(
context.sourceCode.getTokenBefore(
subsetNamedSpecifiers[0],
isOpeningBraceToken,
),
NullThrowsReasons.MissingToken('{', node.type),
);
const commaToken = nullThrows(
context.sourceCode.getTokenBefore(openingBraceToken, isCommaToken),
NullThrowsReasons.MissingToken(',', node.type),
);
const closingBraceToken = nullThrows(
context.sourceCode.getFirstTokenBetween(
openingBraceToken,
node.source,
isClosingBraceToken,
),
NullThrowsReasons.MissingToken('}', node.type),
);
// import DefType, {...} from 'foo'
// ^^^^^^^ remove
removeTypeNamedSpecifiers.push(
fixer.removeRange([commaToken.range[0], closingBraceToken.range[1]]),
);
typeNamedSpecifiersTexts.push(
context.sourceCode.text.slice(
openingBraceToken.range[1],
closingBraceToken.range[0],
),
);
} else {
const namedSpecifierGroups: TSESTree.ImportSpecifier[][] = [];
let group: TSESTree.ImportSpecifier[] = [];
for (const namedSpecifier of allNamedSpecifiers) {
if (subsetNamedSpecifiers.includes(namedSpecifier)) {
group.push(namedSpecifier);
} else if (group.length) {
namedSpecifierGroups.push(group);
group = [];
}
}
if (group.length) {
namedSpecifierGroups.push(group);
}
for (const namedSpecifiers of namedSpecifierGroups) {
const { removeRange, textRange } = getNamedSpecifierRanges(
namedSpecifiers,
allNamedSpecifiers,
);
removeTypeNamedSpecifiers.push(fixer.removeRange(removeRange));
typeNamedSpecifiersTexts.push(
context.sourceCode.text.slice(...textRange),
);
}
}
return {
removeTypeNamedSpecifiers,
typeNamedSpecifiersText: typeNamedSpecifiersTexts.join(','),
};
}
-
JSDoc:
-
Parameters:
fixer: TSESLint.RuleFixer
node: TSESTree.ImportDeclaration
subsetNamedSpecifiers: TSESTree.ImportSpecifier[]
allNamedSpecifiers: TSESTree.ImportSpecifier[]
- Return Type:
{ removeTypeNamedSpecifiers: TSESLint.RuleFix[]; typeNamedSpecifiersText: string; }
- Calls:
nullThrows (from ../util)
context.sourceCode.getTokenBefore
NullThrowsReasons.MissingToken
context.sourceCode.getFirstTokenBetween
removeTypeNamedSpecifiers.push
fixer.removeRange
typeNamedSpecifiersTexts.push
context.sourceCode.text.slice
subsetNamedSpecifiers.includes
group.push
namedSpecifierGroups.push
getNamedSpecifierRanges
typeNamedSpecifiersTexts.join
- Internal Comments:
`getNamedSpecifierRanges(namedSpecifierGroup: TSESTree.ImportSpecifier[], allNamedSpecifiers: TSESTree.ImportSpecifier[]): {¶
removeRange: TSESTree.Range;
textRange: TSESTree.Range;
}`
Code
function getNamedSpecifierRanges(
namedSpecifierGroup: TSESTree.ImportSpecifier[],
allNamedSpecifiers: TSESTree.ImportSpecifier[],
): {
removeRange: TSESTree.Range;
textRange: TSESTree.Range;
} {
const first = namedSpecifierGroup[0];
const last = namedSpecifierGroup[namedSpecifierGroup.length - 1];
const removeRange: TSESTree.Range = [first.range[0], last.range[1]];
const textRange: TSESTree.Range = [...removeRange];
const before = nullThrows(
context.sourceCode.getTokenBefore(first),
NullThrowsReasons.MissingToken('token', 'first specifier'),
);
textRange[0] = before.range[1];
if (isCommaToken(before)) {
removeRange[0] = before.range[0];
} else {
removeRange[0] = before.range[1];
}
const isFirst = allNamedSpecifiers[0] === first;
const isLast = allNamedSpecifiers[allNamedSpecifiers.length - 1] === last;
const after = nullThrows(
context.sourceCode.getTokenAfter(last),
NullThrowsReasons.MissingToken('token', 'last specifier'),
);
textRange[1] = after.range[0];
if ((isFirst || isLast) && isCommaToken(after)) {
removeRange[1] = after.range[1];
}
return {
removeRange,
textRange,
};
}
-
JSDoc:
-
Parameters:
namedSpecifierGroup: TSESTree.ImportSpecifier[]
allNamedSpecifiers: TSESTree.ImportSpecifier[]
- Return Type:
{ removeRange: TSESTree.Range; textRange: TSESTree.Range; }
- Calls:
nullThrows (from ../util)
context.sourceCode.getTokenBefore
NullThrowsReasons.MissingToken
isCommaToken (from ../util)
context.sourceCode.getTokenAfter
fixInsertNamedSpecifiersInNamedSpecifierList(fixer: TSESLint.RuleFixer, target: TSESTree.ImportDeclaration, insertText: string): TSESLint.RuleFix
¶
Code
function fixInsertNamedSpecifiersInNamedSpecifierList(
fixer: TSESLint.RuleFixer,
target: TSESTree.ImportDeclaration,
insertText: string,
): TSESLint.RuleFix {
const closingBraceToken = nullThrows(
context.sourceCode.getFirstTokenBetween(
nullThrows(
context.sourceCode.getFirstToken(target),
NullThrowsReasons.MissingToken('token before', 'import'),
),
target.source,
isClosingBraceToken,
),
NullThrowsReasons.MissingToken('}', target.type),
);
const before = nullThrows(
context.sourceCode.getTokenBefore(closingBraceToken),
NullThrowsReasons.MissingToken('token before', 'closing brace'),
);
if (!isCommaToken(before) && !isOpeningBraceToken(before)) {
insertText = `,${insertText}`;
}
return fixer.insertTextBefore(closingBraceToken, insertText);
}
-
JSDoc:
-
Parameters:
fixer: TSESLint.RuleFixer
target: TSESTree.ImportDeclaration
insertText: string
- Return Type:
TSESLint.RuleFix
- Calls:
nullThrows (from ../util)
context.sourceCode.getFirstTokenBetween
context.sourceCode.getFirstToken
NullThrowsReasons.MissingToken
context.sourceCode.getTokenBefore
isCommaToken (from ../util)
isOpeningBraceToken (from ../util)
fixer.insertTextBefore
fixInsertTypeKeywordInNamedSpecifierList(fixer: TSESLint.RuleFixer, typeSpecifiers: TSESTree.ImportSpecifier[]): IterableIterator<TSESLint.RuleFix>
¶
Code
function* fixInsertTypeKeywordInNamedSpecifierList(
fixer: TSESLint.RuleFixer,
typeSpecifiers: TSESTree.ImportSpecifier[],
): IterableIterator<TSESLint.RuleFix> {
for (const spec of typeSpecifiers) {
const insertText = context.sourceCode.text.slice(...spec.range);
yield fixer.replaceTextRange(spec.range, `type ${insertText}`);
}
}
-
JSDoc:
-
Parameters:
fixer: TSESLint.RuleFixer
typeSpecifiers: TSESTree.ImportSpecifier[]
- Return Type:
IterableIterator<TSESLint.RuleFix>
- Calls:
context.sourceCode.text.slice
fixer.replaceTextRange
fixInlineTypeImportDeclaration(fixer: TSESLint.RuleFixer, report: ReportValueImport, sourceImports: SourceImports): IterableIterator<TSESLint.RuleFix>
¶
Code
function* fixInlineTypeImportDeclaration(
fixer: TSESLint.RuleFixer,
report: ReportValueImport,
sourceImports: SourceImports,
): IterableIterator<TSESLint.RuleFix> {
const { node } = report;
// For a value import, will only add an inline type to named specifiers
const { namedSpecifiers } = classifySpecifier(node);
const typeNamedSpecifiers = namedSpecifiers.filter(specifier =>
report.typeSpecifiers.includes(specifier),
);
if (sourceImports.valueImport) {
// add import named type specifiers to its value import
// import ValueA, { type A }
// ^^^^ insert
const { namedSpecifiers: valueImportNamedSpecifiers } =
classifySpecifier(sourceImports.valueImport);
if (
sourceImports.valueOnlyNamedImport ||
valueImportNamedSpecifiers.length
) {
yield* fixInsertTypeKeywordInNamedSpecifierList(
fixer,
typeNamedSpecifiers,
);
}
}
}
- Parameters:
fixer: TSESLint.RuleFixer
report: ReportValueImport
sourceImports: SourceImports
- Return Type:
IterableIterator<TSESLint.RuleFix>
- Calls:
classifySpecifier
namedSpecifiers.filter
report.typeSpecifiers.includes
fixInsertTypeKeywordInNamedSpecifierList
- Internal Comments:
fixToTypeImportDeclaration(fixer: TSESLint.RuleFixer, report: ReportValueImport, sourceImports: SourceImports): IterableIterator<TSESLint.RuleFix>
¶
Code
function* fixToTypeImportDeclaration(
fixer: TSESLint.RuleFixer,
report: ReportValueImport,
sourceImports: SourceImports,
): IterableIterator<TSESLint.RuleFix> {
const { node } = report;
const { defaultSpecifier, namedSpecifiers, namespaceSpecifier } =
classifySpecifier(node);
if (namespaceSpecifier && !defaultSpecifier) {
// import * as types from 'foo'
// checks for presence of import assertions
if (node.attributes.length === 0) {
yield* fixInsertTypeSpecifierForImportDeclaration(fixer, node, false);
}
return;
}
if (defaultSpecifier) {
if (
report.typeSpecifiers.includes(defaultSpecifier) &&
namedSpecifiers.length === 0 &&
!namespaceSpecifier
) {
// import Type from 'foo'
yield* fixInsertTypeSpecifierForImportDeclaration(fixer, node, true);
return;
}
if (
fixStyle === 'inline-type-imports' &&
!report.typeSpecifiers.includes(defaultSpecifier) &&
namedSpecifiers.length > 0 &&
!namespaceSpecifier
) {
// if there is a default specifier but it isn't a type specifier, then just add the inline type modifier to the named specifiers
// import AValue, {BValue, Type1, Type2} from 'foo'
yield* fixInlineTypeImportDeclaration(fixer, report, sourceImports);
return;
}
} else if (!namespaceSpecifier) {
if (
fixStyle === 'inline-type-imports' &&
namedSpecifiers.some(specifier =>
report.typeSpecifiers.includes(specifier),
)
) {
// import {AValue, Type1, Type2} from 'foo'
yield* fixInlineTypeImportDeclaration(fixer, report, sourceImports);
return;
}
if (
namedSpecifiers.every(specifier =>
report.typeSpecifiers.includes(specifier),
)
) {
// import {Type1, Type2} from 'foo'
yield* fixInsertTypeSpecifierForImportDeclaration(fixer, node, false);
return;
}
}
const typeNamedSpecifiers = namedSpecifiers.filter(specifier =>
report.typeSpecifiers.includes(specifier),
);
const fixesNamedSpecifiers = getFixesNamedSpecifiers(
fixer,
node,
typeNamedSpecifiers,
namedSpecifiers,
);
const afterFixes: TSESLint.RuleFix[] = [];
if (typeNamedSpecifiers.length) {
if (sourceImports.typeOnlyNamedImport) {
const insertTypeNamedSpecifiers =
fixInsertNamedSpecifiersInNamedSpecifierList(
fixer,
sourceImports.typeOnlyNamedImport,
fixesNamedSpecifiers.typeNamedSpecifiersText,
);
if (sourceImports.typeOnlyNamedImport.range[1] <= node.range[0]) {
yield insertTypeNamedSpecifiers;
} else {
afterFixes.push(insertTypeNamedSpecifiers);
}
} else {
// The import is both default and named. Insert named on new line because can't mix default type import and named type imports
// eslint-disable-next-line no-lonely-if
if (fixStyle === 'inline-type-imports') {
yield fixer.insertTextBefore(
node,
`import {${typeNamedSpecifiers
.map(spec => {
const insertText = context.sourceCode.text.slice(
...spec.range,
);
return `type ${insertText}`;
})
.join(
', ',
)}} from ${context.sourceCode.getText(node.source)};\n`,
);
} else {
yield fixer.insertTextBefore(
node,
`import type {${
fixesNamedSpecifiers.typeNamedSpecifiersText
}} from ${context.sourceCode.getText(node.source)};\n`,
);
}
}
}
const fixesRemoveTypeNamespaceSpecifier: TSESLint.RuleFix[] = [];
if (
namespaceSpecifier &&
report.typeSpecifiers.includes(namespaceSpecifier)
) {
// import Foo, * as Type from 'foo'
// import DefType, * as Type from 'foo'
// import DefType, * as Type from 'foo'
const commaToken = nullThrows(
context.sourceCode.getTokenBefore(namespaceSpecifier, isCommaToken),
NullThrowsReasons.MissingToken(',', node.type),
);
// import Def, * as Ns from 'foo'
// ^^^^^^^^^ remove
fixesRemoveTypeNamespaceSpecifier.push(
fixer.removeRange([commaToken.range[0], namespaceSpecifier.range[1]]),
);
// import type * as Ns from 'foo'
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ insert
yield fixer.insertTextBefore(
node,
`import type ${context.sourceCode.getText(
namespaceSpecifier,
)} from ${context.sourceCode.getText(node.source)};\n`,
);
}
if (
defaultSpecifier &&
report.typeSpecifiers.includes(defaultSpecifier)
) {
if (report.typeSpecifiers.length === node.specifiers.length) {
const importToken = nullThrows(
context.sourceCode.getFirstToken(node, isImportKeyword),
NullThrowsReasons.MissingToken('import', node.type),
);
// import type Type from 'foo'
// ^^^^ insert
yield fixer.insertTextAfter(importToken, ' type');
} else {
const commaToken = nullThrows(
context.sourceCode.getTokenAfter(defaultSpecifier, isCommaToken),
NullThrowsReasons.MissingToken(',', defaultSpecifier.type),
);
// import Type , {...} from 'foo'
// ^^^^^ pick
const defaultText = context.sourceCode.text
.slice(defaultSpecifier.range[0], commaToken.range[0])
.trim();
yield fixer.insertTextBefore(
node,
`import type ${defaultText} from ${context.sourceCode.getText(
node.source,
)};\n`,
);
const afterToken = nullThrows(
context.sourceCode.getTokenAfter(commaToken, {
includeComments: true,
}),
NullThrowsReasons.MissingToken('any token', node.type),
);
// import Type , {...} from 'foo'
// ^^^^^^^ remove
yield fixer.removeRange([
defaultSpecifier.range[0],
afterToken.range[0],
]);
}
}
yield* fixesNamedSpecifiers.removeTypeNamedSpecifiers;
yield* fixesRemoveTypeNamespaceSpecifier;
yield* afterFixes;
}
- Parameters:
fixer: TSESLint.RuleFixer
report: ReportValueImport
sourceImports: SourceImports
- Return Type:
IterableIterator<TSESLint.RuleFix>
- Calls:
classifySpecifier
fixInsertTypeSpecifierForImportDeclaration
report.typeSpecifiers.includes
fixInlineTypeImportDeclaration
namedSpecifiers.some
namedSpecifiers.every
namedSpecifiers.filter
getFixesNamedSpecifiers
fixInsertNamedSpecifiersInNamedSpecifierList
afterFixes.push
fixer.insertTextBefore
typeNamedSpecifiers .map(spec => { const insertText = context.sourceCode.text.slice( ...spec.range, ); return
type ${insertText}; }) .join
context.sourceCode.getText
nullThrows (from ../util)
context.sourceCode.getTokenBefore
NullThrowsReasons.MissingToken
fixesRemoveTypeNamespaceSpecifier.push
fixer.removeRange
context.sourceCode.getFirstToken
fixer.insertTextAfter
context.sourceCode.getTokenAfter
context.sourceCode.text .slice(defaultSpecifier.range[0], commaToken.range[0]) .trim
- Internal Comments:
// import * as types from 'foo' // checks for presence of import assertions // import Type from 'foo' (x2) // if there is a default specifier but it isn't a type specifier, then just add the inline type modifier to the named specifiers (x2) // import AValue, {BValue, Type1, Type2} from 'foo' (x2) // import {AValue, Type1, Type2} from 'foo' (x2) // import {Type1, Type2} from 'foo' (x2) // The import is both default and named. Insert named on new line because can't mix default type import and named type imports // eslint-disable-next-line no-lonely-if // import Foo, * as Type from 'foo' (x2) // import DefType, * as Type from 'foo' (x4) // import Def, * as Ns from 'foo' (x4) // ^^^^^^^^^ remove (x4) // import type * as Ns from 'foo' (x2) // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ insert (x2) // import type Type from 'foo' (x2) // ^^^^ insert (x2) // import Type , {...} from 'foo' (x4) // ^^^^^ pick (x2) // ^^^^^^^ remove (x2)
fixInsertTypeSpecifierForImportDeclaration(fixer: TSESLint.RuleFixer, node: TSESTree.ImportDeclaration, isDefaultImport: boolean): IterableIterator<TSESLint.RuleFix>
¶
Code
function* fixInsertTypeSpecifierForImportDeclaration(
fixer: TSESLint.RuleFixer,
node: TSESTree.ImportDeclaration,
isDefaultImport: boolean,
): IterableIterator<TSESLint.RuleFix> {
// import type Foo from 'foo'
// ^^^^^ insert
const importToken = nullThrows(
context.sourceCode.getFirstToken(node, isImportKeyword),
NullThrowsReasons.MissingToken('import', node.type),
);
yield fixer.insertTextAfter(importToken, ' type');
if (isDefaultImport) {
// Has default import
const openingBraceToken = context.sourceCode.getFirstTokenBetween(
importToken,
node.source,
isOpeningBraceToken,
);
if (openingBraceToken) {
// Only braces. e.g. import Foo, {} from 'foo'
const commaToken = nullThrows(
context.sourceCode.getTokenBefore(openingBraceToken, isCommaToken),
NullThrowsReasons.MissingToken(',', node.type),
);
const closingBraceToken = nullThrows(
context.sourceCode.getFirstTokenBetween(
openingBraceToken,
node.source,
isClosingBraceToken,
),
NullThrowsReasons.MissingToken('}', node.type),
);
// import type Foo, {} from 'foo'
// ^^ remove
yield fixer.removeRange([
commaToken.range[0],
closingBraceToken.range[1],
]);
const specifiersText = context.sourceCode.text.slice(
commaToken.range[1],
closingBraceToken.range[1],
);
if (node.specifiers.length > 1) {
yield fixer.insertTextAfter(
node,
`\nimport type${specifiersText} from ${context.sourceCode.getText(
node.source,
)};`,
);
}
}
}
// make sure we don't do anything like `import type {type T} from 'foo';`
for (const specifier of node.specifiers) {
if (
specifier.type === AST_NODE_TYPES.ImportSpecifier &&
specifier.importKind === 'type'
) {
yield* fixRemoveTypeSpecifierFromImportSpecifier(fixer, specifier);
}
}
}
- Parameters:
fixer: TSESLint.RuleFixer
node: TSESTree.ImportDeclaration
isDefaultImport: boolean
- Return Type:
IterableIterator<TSESLint.RuleFix>
- Calls:
nullThrows (from ../util)
context.sourceCode.getFirstToken
NullThrowsReasons.MissingToken
fixer.insertTextAfter
context.sourceCode.getFirstTokenBetween
context.sourceCode.getTokenBefore
fixer.removeRange
context.sourceCode.text.slice
context.sourceCode.getText
fixRemoveTypeSpecifierFromImportSpecifier
- Internal Comments:
fixRemoveTypeSpecifierFromImportDeclaration(fixer: TSESLint.RuleFixer, node: TSESTree.ImportDeclaration): IterableIterator<TSESLint.RuleFix>
¶
Code
function* fixRemoveTypeSpecifierFromImportDeclaration(
fixer: TSESLint.RuleFixer,
node: TSESTree.ImportDeclaration,
): IterableIterator<TSESLint.RuleFix> {
// import type Foo from 'foo'
// ^^^^ remove
const importToken = nullThrows(
context.sourceCode.getFirstToken(node, isImportKeyword),
NullThrowsReasons.MissingToken('import', node.type),
);
const typeToken = nullThrows(
context.sourceCode.getFirstTokenBetween(
importToken,
node.specifiers[0]?.local ?? node.source,
isTypeKeyword,
),
NullThrowsReasons.MissingToken('type', node.type),
);
const afterToken = nullThrows(
context.sourceCode.getTokenAfter(typeToken, { includeComments: true }),
NullThrowsReasons.MissingToken('any token', node.type),
);
yield fixer.removeRange([typeToken.range[0], afterToken.range[0]]);
}
- Parameters:
fixer: TSESLint.RuleFixer
node: TSESTree.ImportDeclaration
- Return Type:
IterableIterator<TSESLint.RuleFix>
- Calls:
nullThrows (from ../util)
context.sourceCode.getFirstToken
NullThrowsReasons.MissingToken
context.sourceCode.getFirstTokenBetween
context.sourceCode.getTokenAfter
fixer.removeRange
- Internal Comments:
fixRemoveTypeSpecifierFromImportSpecifier(fixer: TSESLint.RuleFixer, node: TSESTree.ImportSpecifier): IterableIterator<TSESLint.RuleFix>
¶
Code
function* fixRemoveTypeSpecifierFromImportSpecifier(
fixer: TSESLint.RuleFixer,
node: TSESTree.ImportSpecifier,
): IterableIterator<TSESLint.RuleFix> {
// import { type Foo } from 'foo'
// ^^^^ remove
const typeToken = nullThrows(
context.sourceCode.getFirstToken(node, isTypeKeyword),
NullThrowsReasons.MissingToken('type', node.type),
);
const afterToken = nullThrows(
context.sourceCode.getTokenAfter(typeToken, { includeComments: true }),
NullThrowsReasons.MissingToken('any token', node.type),
);
yield fixer.removeRange([typeToken.range[0], afterToken.range[0]]);
}
- Parameters:
fixer: TSESLint.RuleFixer
node: TSESTree.ImportSpecifier
- Return Type:
IterableIterator<TSESLint.RuleFix>
- Calls:
nullThrows (from ../util)
context.sourceCode.getFirstToken
NullThrowsReasons.MissingToken
context.sourceCode.getTokenAfter
fixer.removeRange
- Internal Comments:
Interfaces¶
SourceImports
¶
Interface Code
interface SourceImports {
reportValueImports: ReportValueImport[];
source: string;
// ImportDeclaration for type-only import only with named imports.
typeOnlyNamedImport: TSESTree.ImportDeclaration | null;
// ImportDeclaration for value-only import only with default imports and/or named imports.
valueImport: TSESTree.ImportDeclaration | null;
// ImportDeclaration for value-only import only with named imports.
valueOnlyNamedImport: TSESTree.ImportDeclaration | null;
}
Properties¶
Name | Type | Optional | Description |
---|---|---|---|
reportValueImports |
ReportValueImport[] |
✗ | |
source |
string |
✗ | |
typeOnlyNamedImport |
TSESTree.ImportDeclaration | null |
✗ | |
valueImport |
TSESTree.ImportDeclaration | null |
✗ | |
valueOnlyNamedImport |
TSESTree.ImportDeclaration | null |
✗ |
ReportValueImport
¶
Interface Code
Properties¶
Name | Type | Optional | Description |
---|---|---|---|
inlineTypeSpecifiers |
TSESTree.ImportSpecifier[] |
✗ | |
node |
TSESTree.ImportDeclaration |
✗ | |
typeSpecifiers |
TSESTree.ImportClause[] |
✗ | |
unusedSpecifiers |
TSESTree.ImportClause[] |
✗ | |
valueSpecifiers |
TSESTree.ImportClause[] |
✗ |
Type Aliases¶
Prefer
¶
FixStyle
¶
Options
¶
MessageIds
¶
type MessageIds = | 'avoidImportType'
| 'noImportTypeAnnotations'
| 'someImportsAreOnlyTypes'
| 'typeOverValue';