consistent-type-imports
Enforce consistent usage of type imports.
Some problems reported by this rule are automatically fixable by the --fix
ESLint command line option.
TypeScript allows specifying a type
keyword on imports to indicate that the export exists only in the type system, not at runtime.
This allows transpilers to drop imports without knowing the types of the dependencies.
See Blog > Consistent Type Exports and Imports: Why and How for more details.
- Flat Config
- Legacy Config
export default tseslint.config({
rules: {
"@typescript-eslint/consistent-type-imports": "error"
}
});
module.exports = {
"rules": {
"@typescript-eslint/consistent-type-imports": "error"
}
};
Try this rule in the playground ↗
Options
This rule accepts the following options:
type Options = [
{
/** Whether to disallow type imports in type annotations (`import()`). */
disallowTypeAnnotations?: boolean;
/** The expected type modifier to be added when an import is detected as used only in the type position. */
fixStyle?:
| 'inline-type-imports'
/** The expected type modifier to be added when an import is detected as used only in the type position. */
| 'separate-type-imports';
/** The expected import kind for type-only imports. */
prefer?:
| 'no-type-imports'
/** The expected import kind for type-only imports. */
| 'type-imports';
},
];
const defaultOptions: Options = [
{
disallowTypeAnnotations: true,
fixStyle: 'separate-type-imports',
prefer: 'type-imports',
},
];
prefer
The expected import kind for type-only imports. Default: "type-imports"
.
Valid values for prefer
are:
type-imports
will enforce that you always useimport type Foo from '...'
except referenced by metadata of decorators. It is the default.no-type-imports
will enforce that you always useimport Foo from '...'
.
Examples of correct code with {prefer: 'type-imports'}
, and incorrect code with {prefer: 'no-type-imports'}
.
import type { Foo } from 'Foo';
import type Bar from 'Bar';
type T = Foo;
const x: Bar = 1;
Open in PlaygroundExamples of incorrect code with {prefer: 'type-imports'}
, and correct code with {prefer: 'no-type-imports'}
.
import { Foo } from 'Foo';
import Bar from 'Bar';
type T = Foo;
const x: Bar = 1;
Open in PlaygroundfixStyle
The expected type modifier to be added when an import is detected as used only in the type position. Default: "separate-type-imports"
.
Valid values for fixStyle
are:
separate-type-imports
will add the type keyword after the import keywordimport type { A } from '...'
. It is the default.inline-type-imports
will inline the type keywordimport { type A } from '...'
and is only available in TypeScript 4.5 and onwards. See documentation here.
- ❌ Incorrect
- ✅ With `separate-type-imports`
- ✅ With `inline-type-imports`
import { Foo } from 'Foo';
import Bar from 'Bar';
type T = Foo;
const x: Bar = 1;
Open in Playgroundimport type { Foo } from 'Foo';
import type Bar from 'Bar';
type T = Foo;
const x: Bar = 1;
Open in Playgroundimport { type Foo } from 'Foo';
import type Bar from 'Bar';
type T = Foo;
const x: Bar = 1;
Open in PlaygrounddisallowTypeAnnotations
Whether to disallow type imports in type annotations (import()
). Default: true
.
Examples of incorrect code with {disallowTypeAnnotations: true}
:
type T = import('Foo').Foo;
const x: import('Bar') = 1;
Open in PlaygroundCaveat: @decorators
+ experimentalDecorators: true
+ emitDecoratorMetadata: true
If you are using experimentalDecorators: false
(eg TypeScript v5.0's stable decorators) then the rule will always report errors as expected.
This caveat only applies to experimentalDecorators: true
The rule will not report any errors in files that contain decorators when both experimentalDecorators
and emitDecoratorMetadata
are turned on.
See Blog > Changes to consistent-type-imports when used with legacy decorators and decorator metadata for more details.
If you are using type-aware linting then we will automatically infer your setup from your tsconfig and you should not need to configure anything.
Otherwise you can explicitly tell our tooling to analyze your code as if the compiler option was turned on by setting both parserOptions.emitDecoratorMetadata = true
and parserOptions.experimentalDecorators = true
.
Comparison with importsNotUsedAsValues
/ verbatimModuleSyntax
verbatimModuleSyntax
was introduced in TypeScript v5.0 (as a replacement for importsNotUsedAsValues
).
This rule and verbatimModuleSyntax
mostly behave in the same way.
There are a few behavior differences:
Situation | consistent-type-imports (ESLint) | verbatimModuleSyntax (TypeScript) |
---|---|---|
Unused imports | Ignored (consider using @typescript-eslint/no-unused-vars ) | Type error |
Usage with emitDecoratorMetadata & experimentalDecorations | Ignores files that contain decorators | Reports on files that contain decorators |
Failures detected | Does not fail tsc build; can be auto-fixed with --fix | Fails tsc build; cannot be auto-fixed on the command-line |
import { type T } from 'T'; | TypeScript will emit nothing (it "elides" the import) | TypeScript emits import {} from 'T' |
Because there are some differences, using both this rule and verbatimModuleSyntax
at the same time can lead to conflicting errors.
As such we recommend that you only ever use one or the other -- never both.
When Not To Use It
If you specifically want to use both import kinds for stylistic reasons, or don't wish to enforce one style over the other, you can avoid this rule.
However, keep in mind that inconsistent style can harm readability in a project. We recommend picking a single option for this rule that works best for your project.
Related To
no-import-type-side-effects
import/consistent-type-specifier-style
import/no-duplicates
with{"prefer-inline": true}