Introduction
Modern bundlers like esbuild, swc, and tsup compile TypeScript orders of magnitude faster than tsc by skipping type checking entirely — they only strip types. The isolatedModules flag ensures your code is compatible with these single-file transpilers by disallowing patterns that require whole-program analysis.
Key Concepts
- Type Stripping: Removing TypeScript syntax (type annotations, interfaces) to produce JavaScript, without performing any type checking.
- isolatedModules: A tsconfig flag that ensures each file can be transpiled independently, without knowledge of other files.
- esbuild / swc: Extremely fast TypeScript transpilers written in Go and Rust respectively.
Real World Context
Vite uses esbuild for development and serves TypeScript files in under 50ms. Next.js uses swc. Both tools strip types without checking them. If your code uses patterns that require cross-file type information (like const enum or namespace merging), these tools silently produce wrong output. isolatedModules catches this at compile time.
Deep Dive
The Two-Tool Approach
Modern projects use two tools for TypeScript:
- tsc — Type checking only (
noEmit: true) - esbuild/swc/tsup — Fast transpilation (type stripping)
json{ "compilerOptions": { "noEmit": true, "isolatedModules": true, "strict": true } }
What isolatedModules Disallows
Patterns that require knowledge of other files:
typescript// 1. Re-exporting types without 'type' keyword export { User } from "./types"; // Error with isolatedModules export type { User } from "./types"; // OK — explicitly a type // 2. const enum (values are inlined from the defining file) const enum Direction { Up, Down } // Error with isolatedModules enum Direction { Up, Down } // OK — regular enum // 3. Namespace merging across files // File A: namespace Foo { export const x = 1; } // File B: namespace Foo { export const y = 2; } // Error — bundlers process files independently
Build Tool Comparison
| Tool | Speed | Type Checking | Language |
|---|---|---|---|
| tsc | Slow (~30s) | Full | TypeScript |
| esbuild | Fast (~0.3s) | None | Go |
| swc | Fast (~0.5s) | None | Rust |
| tsup | Fast (~1s) | Optional via tsc | Go (esbuild) |
tsup Example
bash# Build with tsup (uses esbuild internally) tsup src/index.ts --format cjs,esm --dts # This runs esbuild for transpilation + tsc for .d.ts generation
Common Pitfalls
- Skipping type checking entirely — Fast bundlers do not check types. You must still run
tsc --noEmitin CI or as a pre-commit hook. - Using const enum with bundlers —
const enumrequires cross-file analysis that bundlers cannot do. Use regularenumor string unions instead.
Best Practices
- Always enable isolatedModules — It ensures your code works with any transpiler. This flag is required by Vite, Next.js, and most modern frameworks.
- Run tsc in CI — Use
tsc --noEmitin your CI pipeline for type checking even if you use esbuild/swc for builds.
Summary
- Modern bundlers (esbuild, swc) strip types without checking them — 100x faster than tsc.
isolatedModulesensures code is compatible with single-file transpilers.- Use the two-tool approach: tsc for checking, esbuild/swc for transpilation.
- Always run
tsc --noEmitin CI to catch type errors.
Code Examples
typescript
// With isolatedModules: true
// BAD: re-export without 'type' — bundler can't tell if it's a value or type
export { User } from "./types"; // Error
// GOOD: explicit type re-export
export type { User } from "./types"; // OK
// BAD: const enum requires cross-file analysis
const enum Color { Red, Green, Blue } // Error
// GOOD: regular enum or string union
enum Color { Red, Green, Blue } // OK
type Color = "red" | "green" | "blue"; // Also OK