Skip to Content
Export Types From a Module Without Runtime Code
Intermediate
0 likes
Tutorials

Export Types From a Module Without Runtime Code

Compare import type, the inline type keyword and a plain import, and see which ones leave code in the compiled file. After this, a types-only import cannot load a module with side effects.

A types file should cost nothing when the program runs. Often it does cost something: an import that is only there for a type can still make Node load the whole module behind it, side effects included. This page shows the three ways to write a type import, what each one leaves in the compiled output, and how to prove it. The course lesson on modules introduces import type. This is the practical follow-up, with re-exports.

Step 1: a module with a side effect

export type User = { id: number; name: string };

export function makeUser(id: number, name: string): User {
  return { id, name };
}

console.log("user module loaded");

The last line stands in for anything a real module does when it loads: open a connection, read a file, register a handler. A file that only needs the User type should never trigger it.

Step 2: import type leaves nothing behind

import type { User } from "./user.js";

const ana: User = { id: 1, name: "Ana" };
console.log(ana.name);
Ana

Only Ana is printed. The line user module loaded never appears, because the compiled app.js has no import at all.

cat app.js
const ana = { id: 1, name: "Ana" };
console.log(ana.name);
export {};

Step 3: the inline type keyword still loads the module

You can also mark a single name inside braces. That reads well, and it is the form many style guides prefer. With verbatimModuleSyntax turned on, it behaves differently from import type.

import { type User } from "./user.js";

const ana: User = { id: 1, name: "Ana" };
console.log(ana.name);
user module loaded
Ana

The module loaded. The compiled file keeps an empty import, import {} from "./user.js", because under verbatimModuleSyntax the compiler keeps every import statement exactly as you wrote it and only removes the names marked type. An import with nothing left in the braces still runs the module.

cat app.js
import {} from "./user.js";
const ana = { id: 1, name: "Ana" };
console.log(ana.name);

If the file only needs types, write import type { User } from "./user.js", and the whole line goes.

Step 4: a plain import is an error under the strict flag

import { User } from "./user.js";

const ana: User = { id: 1, name: "Ana" };
console.log(ana.name);
plain.ts(1,10): error TS1484: 'User' is a type and must be imported using a type-only import when 'verbatimModuleSyntax' is enabled.

This is the setting that tsc --init writes, and it exists to stop that guess. Without the flag, TypeScript decides on its own that User is only a type and drops the import. Other tools that compile one file at a time, such as esbuild or Babel, cannot always make that decision, which is why the explicit form is safer.

Step 5: re-export types as types

A barrel file, the index.ts that re-exports from several files, should follow the same rule.

export type { User } from "./user.js";
export { makeUser } from "./user.js";
npx tsc index.ts
cat index.js
export { makeUser } from "./user.js";

The export type line is gone from the output. Only the value export remains. A barrel that has nothing but export type lines compiles to an empty module, and no file behind it loads.

⚠ Warning: A plain export * from "./user.js" re-exports values too, so importing the barrel always loads user.js, side effect included. If a barrel must stay free of side effects, list the type exports with export type and keep the value exports in a separate file.

Common mistakes

  • Using import { type X } when the whole import is only types. The braces stay, and the module still loads.
  • Using export * on files that run code when they load.
  • Keeping types and constants in the same file and expecting the constants to be erased. A constant is a value.

Related course: TypeScript Intermediate.

About

Compare import type, the inline type keyword and a plain import, and see which ones leave code in the compiled file. After this, a types-only import cannot load a module with side effects.

Responsible
Mary Q
Last Update
10/11/2026
Reading Time
4 min
Was this helpful?
​ 0 ​ 0

Comments (0)

Sign in to comment

No comments yet.