Importing Types
Importing types is slightly different than importing runtime JavaScript code. If you don't do it correctly, it can negatively impact your bundler's ability to optimize your code.
The problem
In standard JavaScript, an import statement tells the bundler (like Webpack or Vite) to include that file in the final build.
However, TypeScript types are completely erased during compilation. They do not exist at runtime. If you import a module only for its types, but you use standard import syntax, the bundler might accidentally include the entire runtime module in the final JS bundle, causing bloat.
Type-only imports
To solve this, TypeScript introduced type-only imports using the type modifier.
// Good: The bundler knows this is purely for the compiler
import type { User, Order } from "./models";
function processOrder(user: User, order: Order) {
// ...
}
This acts as a strict guarantee. The compiler will completely strip this import statement from the emitted .js file. If you try to use User as a runtime value (e.g., new User()), the compiler will block it.
Inline type imports
You can also mix value and type imports from the same file using inline type modifiers.
// 'fetchData' is a runtime function, 'DataPayload' is a compile-time type
import { fetchData, type DataPayload } from "./api";
const payload: DataPayload = await fetchData();
Enforcing type imports in the team
If you want to ensure your team always uses import type where appropriate, you can enforce it.
Turn on importsNotUsedAsValues: "error" in your tsconfig.json (or verbatimModuleSyntax: true in TS 5.0+).
This forces the compiler to throw an error if you import a type without the type keyword.
Always explicitly use
import type(or inlinetypemodifiers) when importing interfaces and types. It guarantees that the bundler will completely strip the import, keeping your production bundle small.