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.
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.
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:
// 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:
// 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:
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:
// 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:
// 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.
// 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.
| Style | When 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
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.
Frequently Asked Questions
Does import type affect runtime behavior?
When should I use import type instead of a regular import?
Can I mix type and value imports in one statement?
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.
More in this topic
TypeScript Compiler Explained
The TypeScript compiler (tsc) turns .ts files into .js files while checking types. Learn what it does, how to use it, and how it fits your workflow.
Set Up ESLint for TypeScript
Install and configure ESLint with typescript-eslint to catch bugs and enforce consistent code style in your TypeScript project.
Pick Utility Type in TypeScript
Pick creates a new type by selecting only the properties you name from an existing type. Use it to build focused types for specific views, API responses, or function parameters.