why single-file transpilers require this flag
esbuild, SWC, and Babel compile each file on its own, without type information from other files. isolatedModules flags exactly the cases that would break under that constraint.
Table of Contents
- 1. What isolatedModules technically means
- 2. Why modern build tools transpile files individually
- 3. Re-exporting types: requiring export type
- 4. const enum: why it fails under isolatedModules
- 5. Namespace merging and other forbidden patterns
- 6. isolatedModules vs. verbatimModuleSyntax: the relationship
- 7. Common error messages and how to fix them
- 8. Configuration in Vite, esbuild, and SWC projects
- 9. Checklist for migrating an existing project
- 10. Summary
- 11. FAQ
1. What isolatedModules technically means
isolatedModules is not a transpiler flag itself, it is a pure type-checking flag: it changes nothing about tsc's output, but reports an error as soon as code is written that could not compile correctly if each file were processed in isolation, with no knowledge of other files.
The background: tsc itself does not need this restriction, because when compiling it always sees the full program with all type information. Other tools do not, and that is exactly what the flag is for.
Enabling isolatedModules makes tsc effectively simulate the constraints of a single-file transpiler, warning early about code that looks fine in the IDE but would break in the actual build.
2. Why modern build tools transpile files individually
Tools like esbuild, SWC, or the Babel TypeScript preset strip type annotations purely syntactically, without analyzing the rest of the project. That is the source of their enormous speed: no type resolution, no cross-file knowledge, just text replacement based on local syntax.
That speed has a cost: the transpiler cannot know whether an imported symbol is a type or a value if that information is not directly visible at the import site. For tsc itself this is not a problem, because the full type graph is known.
That is exactly why pairing tsc for pure type checking (with noEmit) with a separate, fast transpiler for the actual JavaScript output has become the standard workflow in Vite, esbuild-based setups, and modern monorepo pipelines.
3. Re-exporting types: requiring export type
A classic case caught by isolatedModules is re-exporting a pure type through a plain export { Foo }, when Foo is exclusively an interface or type alias. A single-file transpiler, lacking type information, cannot know this line must be stripped entirely at runtime.
The fix is export type { Foo }, explicit syntax that tells the transpiler, at the text level alone, that nothing exists here at runtime and the line can safely disappear during compilation.
Since TypeScript 5.0, isolatedModules also correctly handles mixed exports, where a module exports both values and types under the same identifier, which previously could trigger confusing false positives.
// types.ts
export interface User { id: string; name: string }
export const DEFAULT_ROLE = "guest";
// index.ts: without isolatedModules this would compile, but
// a single-file transpiler wouldn't know User is type-only:
export { User, DEFAULT_ROLE }; // error with isolatedModules
// Correct:
export type { User };
export { DEFAULT_ROLE };
4. const enum: why it fails under isolatedModules
const enum is a pure compile-time optimization: tsc replaces every usage with its concrete value (inlining) and generates no object at runtime. That requires the compiler to know the enum's definition from another file while compiling the current one.
A single-file transpiler never sees that definition and cannot perform the inlining, so it would either produce a runtime error or leave the enum access unchanged, pointing at an object that does not exist at runtime.
With isolatedModules enabled, TypeScript flags every usage of const enum outside its own file as an error. The usual fix is a plain enum or an object literal with as const, offering the same type comfort without the inlining problem.
// Problematic under isolatedModules:
export const enum Status { Active, Archived }
// Robust alternative, works the same everywhere:
export const Status = { Active: "active", Archived: "archived" } as const;
export type Status = (typeof Status)[keyof typeof Status];
5. Namespace merging and other forbidden patterns
namespace declarations that merge across multiple files into a shared namespace (declaration merging) also require knowledge of the whole codebase, and are restricted under isolatedModules unless they contain types exclusively.
A further edge case involves files with no import or export at all: TypeScript treats them as global scripts by default. Under isolatedModules, that can trigger warnings, because a single-file transpiler cannot reliably distinguish an export-less module from a true global script.
In practice these edge cases affect real application code far less often than the export type issue, but they are often the most tedious parts when migrating older codebases with historically grown namespace structures.
6. isolatedModules vs. verbatimModuleSyntax: the relationship
verbatimModuleSyntax, introduced in TypeScript 5.0, goes further than isolatedModules: while isolatedModules only prevents code that would break under single-file transpilation, verbatimModuleSyntax additionally forces import and export statements to appear in the output exactly as written.
In practice, most modern projects now enable both flags together, since verbatimModuleSyntax essentially subsumes the rules of isolatedModules and enforces additional clarity about the module system.
Anyone setting up a new project today should reach for verbatimModuleSyntax directly; isolatedModules remains relevant for existing projects and older TypeScript versions that do not yet support the newer flag.
7. Common error messages and how to fix them
The error Re-exporting a type when isolatedModules is enabled requires using export type is the most common one, resolved by adding the type modifier to the export, either per symbol or for the whole export statement.
For const enum errors, the message effectively states that const enums cannot be used across module boundaries. The only real fix is switching to a plain enum or an object literal with as const; refactoring is usually worth doing project-wide rather than piecemeal.
It matters not to blanket-suppress these errors with // @ts-ignore, because the problem is real in exactly the cases the compiler reports: the build tool's output would actually be broken, not just the type checker being overly cautious.
8. Configuration in Vite, esbuild, and SWC projects
In Vite projects, isolatedModules: true has been the recommended default in tsconfig for several major versions, because Vite uses esbuild internally for TypeScript transformation and therefore has exactly the single-file constraints the flag warns about.
In SWC-based setups, such as Next.js, the same logic applies: SWC also transpiles files in isolation, so isolatedModules should be enabled in the underlying tsconfig, even though SWC itself does not read the flag directly.
For pure tsc-only projects without a separate fast transpiler, isolatedModules is optional, but harmless: it merely forces cleaner code that keeps working without surprises if the build tool changes later.
9. Checklist for migrating an existing project
Before enabling it project-wide, it pays to do a dry run: set isolatedModules in tsconfig and run tsc --noEmit to get a full error list of affected spots before actually switching to a different transpiler.
The table below summarizes the most common forbidden patterns and their fix.
| Pattern | Problem under isolatedModules | Fix | Affected since |
|---|---|---|---|
| export { Foo } for a pure type | Transpiler doesn't know the type status | export type { Foo } | TS 3.8+ |
| const enum across file boundaries | Inlining impossible without context | enum or as const object | TS 3.8+ |
| namespace merging across files | Requires project-wide knowledge | Use modules instead of namespaces | TS 3.8+ |
| File with no import/export | Ambiguous: module or global script | Add at least one export | TS 3.8+ |
Mironsoft
TypeScript migration, type safety, and team onboarding
A JavaScript codebase without type safety, but no time for a full migration?
We migrate existing JavaScript projects to TypeScript step by step, set up strict compiler settings cleanly, and bring teams to the same type-safety level with code reviews and style guides.
Migration Roadmap
Plan and execute a gradual JS-to-TS migration without big-bang risk.
Strict Mode Rollout
Set up tsconfig.json, ESLint rules, and CI checks for lasting type safety.
Team Onboarding
Bring developers up to speed on TypeScript best practices with workshops and reviews.
10. Summary
isolatedModules
Flag type
Pure type-checking flag, does not alter tsc's own output.
Target audience
Projects using esbuild, SWC, Babel, or Vite as the transpiler.
Most common error
Missing export type when re-exporting a pure type.
Successor
verbatimModuleSyntax covers the same cases plus more.