Type Only Imports in TypeScript

Type-only imports tell TypeScript and your bundler that an import is only needed at compile time. Learn import type, inline type specifiers, and when each style matters.

6 min read

Type only imports in TypeScript tell the compiler that you only need the imported thing for type checking, never as a runtime value. The entire import statement is erased from the compiled JavaScript. This exists because TypeScript types are a compile-time concept: they have no representation in the JavaScript that runs in the browser or Node.js.

Type-only import compile-time flow

The diagram shows the two paths an import can take. A regular import is type-checked and then emitted into the JavaScript output.

An import type is type-checked and then completely removed. The compiled JavaScript contains no trace of it.

Type-only imports serve three purposes: they make the intent explicit, they help bundlers eliminate dead code, and they are required when verbatimModuleSyntax is enabled in tsconfig.

Import Type Syntax

The import type statement imports only types. It can never bring in a runtime value, which makes it the most explicit way to declare that an import is compile-time only.

Here is a module that exports both a type and a function:

typescripttypescript
// models.ts
export interface User {
  id: string;
  name: string;
  email: string;
}
 
export function createUser(name: string, email: string): User {
  return { id: crypto.randomUUID(), name, email };
}

A consuming file only needs the User shape for a parameter annotation, so it imports the interface with import type instead of a regular import:

typescripttypescript
// dashboard.ts
import type { User } from "./models.js";
 
function renderUser(user: User): string {
  return `${user.name} (${user.email})`;
}

Compiling dashboard.ts strips the import line entirely, since User never exists as a value. Only the function that uses it survives:

javascriptjavascript
function renderUser(user) {
  return `${user.name} (${user.email})`;
}

The compiled output shows that the import disappeared entirely. TypeScript checked that the User interface has name and email properties, then removed everything related to the import. The function itself survives because it is a runtime value.

If you accidentally try to use a type-only import as a value, TypeScript reports an error before compilation completes. It tells you that the imported name cannot be used as a value because it was imported using import type. This error protects you from writing code that would fail at runtime because the import was erased.

Inline Type Specifiers

When you need both types and values from the same module, use the inline type keyword on individual import specifiers. Here is a module that exports types alongside functions:

typescripttypescript
// models.ts
export interface User {
  id: string;
  name: string;
}
 
export function createUser(name: string): User {
  return { id: crypto.randomUUID(), name };
}
 
export function deleteUser(id: string): void {
  console.log(`Deleted ${id}`);
}

Now import everything in one line, marking only the type as type-only so it gets erased while the functions survive:

typescripttypescript
// app.ts
import { type User, createUser, deleteUser } from "./models.js";
 
const user: User = createUser("Ada");
deleteUser(user.id);

In this single import statement, User is marked as type-only and is erased from the output. The functions createUser and deleteUser are value imports and survive in the compiled JavaScript. The inline type keyword works the same way in export lists when re-exporting, giving you fine-grained control over what lands in the runtime output.

Export Type

Just as import type marks an import as type-only, export type marks an export as type-only. This is most useful in barrel files that re-export from multiple modules.

typescripttypescript
// types/index.ts
export type { User, CreateUserInput } from "./user-types.js";
export type { Post, PostStatus } from "./post-types.js";
export type { Comment } from "./comment-types.js";

All of these re-exports are erased from the compiled JavaScript. The barrel file produces an empty module at runtime, but TypeScript resolves all the types from it at compile time. For more on organizing barrel files, see barrel files in TypeScript.

Verbatim Module Syntax

The verbatimModuleSyntax compiler option changes the rules around type imports. When enabled, TypeScript requires you to use import type and export type for anything that is only used as a type.

Regular import and export statements are reserved exclusively for values under this setting. For background on how regular imports work, see ES modules in TypeScript.

Without verbatimModuleSyntax, TypeScript automatically detects when an import is only used as a type and elides it from the output anyway. This is convenient but can hide the distinction between value and type imports. With verbatimModuleSyntax enabled, you must be explicit: if a name is only used in type positions, mark it with the type keyword in the import or export statement.

Enabling verbatimModuleSyntax is recommended for new projects. It makes the type and value distinction visible in every import statement and prevents surprises when switching between bundlers and module formats. The cost is a slightly more verbose import style, but the clarity it provides makes the tradeoff worthwhile.

When Each Style Matters

The right style depends on what you are importing and how you plan to use it.

StyleWhen to use
import type { X }You only need types from the module
import { type X, y }You need both types and values from the same module
export type { X }Re-exporting types from a barrel file
Regular import { X }You need runtime values as well as types

The inline type keyword is especially useful when a module exports a mix of types and values. Instead of splitting into two import statements, you can express the intent in a single line. This keeps imports clean while still giving the compiler and your teammates precise information about what is runtime code and what is compile-time only.

Common Mistakes

Using a type-only import as a value. TypeScript catches this and reports an error. The fix depends on what you need: if you need the runtime value, switch to a regular import. If you only need the type, keep using the value only in type positions like annotations and type arguments.

Mixing import type with a default and named imports in one statement. This is not allowed by the syntax. A type-only import declaration may not declare both a default import and named bindings. Split it into two statements, or use default as a named binding within the import type.

Relying on automatic elision instead of being explicit. TypeScript is smart about removing unused type imports, but being explicit with import type makes your intent visible to every developer and tool that reads the code. It also future-proofs your code if verbatimModuleSyntax becomes the default in a future TypeScript version.

Rune AI

Rune AI

Key Insights

  • import type brings in only types and is completely erased from the compiled JavaScript.
  • The inline type keyword lets you mark individual specifiers as type-only within a mixed import.
  • export type ensures re-exported types are erased and not accidentally included in runtime output.
  • With verbatimModuleSyntax enabled, you must use import type for any import that is only used as a type.
  • import type helps bundlers tree-shake more aggressively by removing imports with no runtime footprint.
RunePowered by Rune AI

Frequently Asked Questions

Does import type affect runtime behavior?

No. import type statements are completely erased during compilation. They produce zero JavaScript. Their only purpose is to tell TypeScript about a type from another module.

When should I use import type instead of a regular import?

Use import type when you only need types from a module and never use its runtime values. This makes the intent clear, helps bundlers with tree-shaking, and avoids accidental runtime imports of values that should stay type-only.

Can I mix type and value imports in one statement?

Yes. Write import { type User, createUser } from './models.js' to import User as a type-only specifier and createUser as a value, all in one line.

Conclusion

Type-only imports are a precise way to tell TypeScript that an import should not survive compilation. Use import type for statements that only bring in types, use the inline type keyword when mixing types and values in one import, and use export type when re-exporting types. These patterns keep your compiled output clean and your intent explicit.