isolatedModules
Flags code that a single-file transpiler such as Babel or esbuild cannot compile correctly.
"isolatedModules": boolean- Added in TypeScript 1.5
Parameters
| Parameter | Type | Description |
|---|---|---|
$trueoptional | boolean | Bans re-exporting a type without a type modifier, ambient const enum references, namespaces in non-module (global script) files, and other constructs a file-at-a-time transpiler cannot resolve. |
$falseoptional | boolean | Default, unless verbatimModuleSyntax is enabled, which implies this flag. |
Return value
Reports errors on constructs whose correct compilation needs whole-program type information. Does not change emit or runtime behaviour.
Errors
Error TS1205 when re-exporting a type without the type modifier; TS1209 when referencing an ambient const enum member; and an error on a namespace in a global script file, which suggests setting moduleDetection to force or adding an empty `export {}`.
Changelog
- PHP 5.0verbatimModuleSyntax implies isolatedModules, since verbatim emit is precisely what a single-file transpiler can reproduce.
- PHP 5.5isolatedDeclarations extends the same idea to .d.ts generation, requiring explicit return types so declarations can be emitted file by file.
isolatedModules does not change a single byte of output. It is a compatibility check: it warns you when you write something that tsc can compile — because it sees the whole program — but a file-at-a-time transpiler cannot.
Why the distinction exists
Babel, esbuild, swc and ts.transpileModule process one file with no type information at all. Given export { someType }, such a tool has no way to know that someType is a type with no runtime value, so it emits a real export of a binding that does not exist. The result is a module-not-found or undefined-export error at runtime, from code that compiled cleanly.
What it bans
- Re-exporting a type without
type— writeexport type { Cents }orexport { type Cents }. - Ambient
const enummembers —tscinlinesNumbers.Zeroas0; a lone file cannot, and leaves a dangling reference. - Namespaces in a global script file — allowed only in files that already have an
importorexport. (.d.tsfiles are exempt.)
When to turn it on
Any project whose real build is done by Vite, Next.js, esbuild or Babel while tsc only type checks. Turning it on late is uncomfortable but mechanical; leaving it off means the two toolchains can disagree in production.
Example
// tsconfig.json
// { "compilerOptions": { "isolatedModules": true } }
import { formatCents, type Cents } from "./money.js";
// export { Cents }; // error TS1205: re-exporting a type needs 'export type'
export type { Cents }; // ✔ explicitly type-only, erased
export { formatCents }; // ✔ a real runtime value
// declare const enum Size { S = 1 } // referencing Size.S is error TS1209
enum Size { S = 1 } // a plain enum is fine: it emits an object
console.log(Size.S, formatCents(500));
// -> 1 $5.00When to use it
- Preparing a codebase for a switch from tsc to esbuild, swc or Vite.
- Preventing const enum usage that silently breaks under a Babel pipeline.
- Catching type re-exports that a bundler would emit as broken runtime exports.
- Satisfying the prerequisite implied by verbatimModuleSyntax.
Frequently asked questions
What does isolatedModules actually change?
Nothing about the emitted JavaScript — it only adds errors for constructs a single-file transpiler such as Babel or esbuild cannot compile correctly. Enabling it is therefore risk-free for an existing build and only surfaces latent incompatibilities.
How do I fix "Re-exporting a type when isolatedModules is enabled requires using export type"?
Change export { Foo } to export type { Foo }, or mark the individual name with export { type Foo, bar }. The modifier tells any transpiler that the name has no runtime value without needing type information.
Can I use const enum with isolatedModules?
You can declare a local const enum, but referencing an ambient (declared) const enum member is error TS1209 because only tsc can inline those values. Use a regular enum, or an as const object, when a bundler does your transpilation.
More examples
Why a type re-export is unsafe file-by-file
tsc knows SomeType is a type and elides it; esbuild, seeing one file with no type information, cannot. The type modifier moves that knowledge into the syntax so any transpiler gets it right, which is the entire point of the flag.
// someModule.ts
export type SomeType = { id: number };
export function someFunction() { return 42; }
// index.ts — as written, without isolatedModules:
// import { someType, someFunction } from "./someModule.js";
// export { someType, someFunction };
//
// A single-file transpiler emits BOTH names, including someType,
// which has no runtime value -> SyntaxError or undefined export.
import { type SomeType, someFunction } from "./someModule.js";
export type { SomeType };
export { someFunction };
console.log(someFunction());
// -> 42const enum and the namespace rule
Both errors point at the same limitation: the transpiler sees one file and cannot know what the rest of the program declares. Note that .d.ts files are exempt from the namespace rule, so ambient declaration files still work normally.
// 1. Ambient const enums are inlined by tsc, and only by tsc.
// declare const enum Numbers { Zero = 0, One = 1 }
// console.log(Numbers.Zero + Numbers.One);
// tsc emits: console.log(0 + 1);
// esbuild emits: console.log(Numbers.Zero + Numbers.One); -> ReferenceError
// Under isolatedModules this is error TS1209.
// 2. A namespace needs the file to be a module.
export {}; // this line is what makes the namespace below legal
namespace Instantiated {
export const x = 1;
}
console.log(Instantiated.x);
// -> 1
// Without `export {}`: "Namespaces are not allowed in global script files
// when 'isolatedModules' is enabled." Set moduleDetection to "force"
// to treat every file as a module instead.
Discussion