Skip to content

typeImports

Reports imports that do not match the configured type import style.

✅ This rule is included in the ts stylistic presets.

TypeScript’s import type syntax marks imports that exist only for type checking. Those imports are removed from emitted JavaScript without requiring the transpiler to understand the imported module’s types.

This rule enforces a consistent style for imports whose bindings are only referenced in type positions. By default it reports regular imports that should use import type. It can also enforce the opposite style with regular imports instead of import type.

Using a consistent type import style makes each import’s runtime behavior clear. The default preference also helps single-file transpilers remove type-only imports without needing type information from the whole project.

import { User } from "./types";
type Admin = User & { isAdmin: boolean };
import { User, createUser } from "./users";
type Admin = User & { isAdmin: boolean };
const user = createUser();

With the default fixStyle: "separate-type-imports", mixed imports are split into a separate import type declaration.

import User from "./types";
type Admin = User & { isAdmin: boolean };
import * as Types from "./types";
type Admin = Types.User & { isAdmin: boolean };

References inside typeof type queries are safe to convert to import type.

import User from "./types";
type UserConstructor = typeof User;
type UserName = typeof User.name;

Imports that are only re-exported with export type can use import type.

import { User } from "./types";
export type { User };

Regular export { User } and export default User may create runtime exports, so this rule treats them as value usages unless the import is already type-only.

Computed property names in type-only declarations can reference values for type checking. This rule allows those imports to become type-only when the containing type is erased.

import * as constants from "./constants";
type Values = {
[constants.value]: readonly string[];
};
{
"fixStyle": "separate-type-imports",
"prefer": "type-imports"
}

prefer controls whether the rule expects type-only imports or regular imports for bindings used only as types. It defaults to "type-imports".

  • "type-imports" reports regular imports that are used only as types.
  • "no-type-imports" reports import type declarations and inline type import specifiers.

Examples of correct code with { prefer: "type-imports" }, and incorrect code with { prefer: "no-type-imports" }:

import type { User } from "./types";
import type UserModel from "./model";
type Admin = User & { model: UserModel };

Examples of incorrect code with { prefer: "type-imports" }, and correct code with { prefer: "no-type-imports" }:

import { User } from "./types";
import UserModel from "./model";
type Admin = User & { model: UserModel };

With { prefer: "no-type-imports" }, inline type specifiers are also reported.

import { type User, createUser } from "./users";
type Admin = User & { isAdmin: boolean };
const user = createUser();

fixStyle controls how auto-fixes are written when prefer is "type-imports". It defaults to "inline-type-imports".

  • "inline-type-imports" adds inline type specifiers such as import { type User } from "./types" when TypeScript syntax allows it.
  • "separate-type-imports" adds top-level type-only declarations such as import type { User } from "./types".
import { User } from "./types";
type Admin = User & { admin: boolean };

Default imports and namespace imports cannot use inline type specifiers. They are fixed to top-level import type declarations even when fixStyle is "inline-type-imports".

import type User from "./types";
import type * as Types from "./types";

This rule does not report import() type annotations. Use top-level import type declarations if your project wants to avoid inline import() type references.

Examples of code this rule allows:

type User = import("./types").User;
let value: import("./types").User;

When a file contains decorators and the TypeScript compiler options enable both experimentalDecorators and emitDecoratorMetadata, TypeScript can emit runtime metadata for types that appear only in type annotations. In that configuration, this rule does not report import declarations from decorated files because changing those imports to import type can change emitted decorator metadata.

This caveat only applies to legacy experimental decorators with emitted decorator metadata. Files using stable decorators without emitDecoratorMetadata are reported normally.

TypeScript’s verbatimModuleSyntax compiler option also distinguishes type-only imports from value imports. This rule and verbatimModuleSyntax overlap, but they are not identical.

SituationThis ruleverbatimModuleSyntax
Unused importsIgnores themCan report a compiler error
Files with legacy decorator metadataIgnores decorated filesUses TypeScript’s emit behavior
Feedback locationLint report with fixesCompiler error
Inline imports such as import { type User } from "./types"Allowed by defaultCan emit an empty runtime import in some configurations

Use this rule when you want lint feedback and auto-fixes for import style. Use verbatimModuleSyntax when you want the TypeScript compiler to enforce that syntax during builds.

If your project intentionally keeps type-only dependencies in regular imports, configure { prefer: "no-type-imports" } or disable this rule. If your project already relies on TypeScript’s verbatimModuleSyntax errors for import consistency, you may not need this rule because the compiler will reject many related import forms. Large existing projects may also prefer to adopt this rule gradually because it can change many import declarations at once.

  • importTypeSideEffects reports imports such as import { type A, type B } that may emit empty side-effect imports under verbatimModuleSyntax.
Made with ❤️‍🔥 around the world by the Flint team and contributors.