TypeScript Isolated Declarations: What It Actually Replaces in Your Build Pipeline
Most TypeScript build bottlenecks stem from declaration emit, not type checking. Learn what isolated declarations actually replaces in your pipeline and how to remove tsc --declaration from your build.
TypeScript Isolated Declarations: What It Actually Replaces in Your Build Pipeline
Most TypeScript build bottlenecks stem from declaration emit, not type checking. Teams wait minutes for tsc --declaration to complete while bundlers like esbuild and Rollup sit idle, unable to generate .d.ts files themselves. The TypeScript team ships this feature in 5.5, and the immediate question developers ask is wrong: they ask if it replaces type checking. It doesn't. It replaces the slowest part of your build pipeline: sequential declaration file generation that blocks parallel builds.
The confusion is understandable. TypeScript's compiler has done two jobs for years: checking types and emitting declarations. Isolated declarations separates these concerns, enabling fast bundlers to emit .d.ts files without invoking the type checker. The performance impact is dramatic in monorepos where dozens of packages wait for declaration emit before downstream packages can build.
Key Takeaways
- Isolated declarations replaces
tsc --declarationin your build step, nottsc --noEmitin CI - Fast bundlers (esbuild, Rollup, Vite) can now emit
.d.tsfiles in parallel without TypeScript's type checker - Migration requires explicit return types and parameter types — the compiler cannot infer across module boundaries
- Build time improvements scale with project count: 10+ packages in a monorepo see 3-5x faster declaration emit
- Type checking remains a separate CI concern; isolated declarations optimizes build pipelines, not validation pipelines
What Isolated Declarations Actually Replaces in Your Pipeline
Isolated declarations replaces the declaration emit phase of your TypeScript build. Traditional pipelines run tsc --declaration to generate .d.ts files alongside JavaScript output. This process is slow because the compiler must analyze cross-file dependencies to infer types. A function without an explicit return type requires the compiler to trace through every code path, resolve imported types, and compute the narrowest type that matches all branches.
Fast bundlers like esbuild transpile TypeScript faster than tsc by skipping type analysis entirely. They strip types and emit JavaScript in milliseconds. But they cannot emit declarations because computing .d.ts output requires type information. Teams end up running two separate processes: esbuild for speed, then tsc --emitDeclarationOnly for declarations. The second step is the bottleneck.
Isolated declarations enables single-file declaration emit. When your code includes explicit type annotations on public APIs, a bundler can generate the .d.ts file from that single source file without analyzing imports. The compiler flag isolatedDeclarations: true enforces this constraint at compile time, rejecting code that would require cross-file inference.
%% alt: Traditional TypeScript build pipeline showing sequential tsc declaration emit blocking parallel builds
flowchart TD
Source["Source: .ts files"]
Bundle["Bundle: esbuild (fast)"]
Declarations["Declarations: tsc --declaration (slow)"]
Output["Output: .js + .d.ts"]
Source --> Bundle
Source --> Declarations
Bundle --> Output
Declarations --> Output
style Declarations stroke:#ef4444,fill:#450a0a,color:#fca5a5
classDef userAction fill:#142544,stroke:#7c9cf0,color:#eaf2ff
classDef framework fill:#0b3b2e,stroke:#34d399,color:#d1fae5
classDef dataStore fill:#3a2f0b,stroke:#fbbf24,color:#fef3c7
class Bundle framework
class Declarations framework
class Output dataStore
The diagram shows the traditional pipeline where declarations block output. With isolated declarations, the bundler emits both JavaScript and declarations in a single pass.
Before and After: Removing tsc --declaration from Your Build
Traditional build scripts run two commands. This example shows a typical monorepo package build:
// package.json (before isolated declarations)
{
"scripts": {
"build": "esbuild src/index.ts --bundle --outdir=dist && tsc --emitDeclarationOnly --outDir dist"
},
"compilerOptions": {
"declaration": true,
"emitDeclarationOnly": false
}
}The esbuild step completes in 50ms. The tsc --emitDeclarationOnly step takes 3-8 seconds because it must analyze the entire dependency graph to infer types for unannotated exports. Multiply this across 20 packages and build time exceeds two minutes.
With isolated declarations enabled, the bundler handles both steps:
// package.json (after isolated declarations)
{
"scripts": {
"build": "esbuild src/index.ts --bundle --outdir=dist --declaration"
},
"compilerOptions": {
"isolatedDeclarations": true,
"declaration": true
}
}The single esbuild command now emits JavaScript and declarations in 80ms total. The 3-8 second tsc invocation disappears. In a monorepo with 20 packages building in parallel, total build time drops from 2 minutes to 15 seconds.

The performance gain compounds in watch mode. Traditional pipelines re-run tsc --emitDeclarationOnly on every change, even when only implementation details change. Isolated declarations emit only when public API signatures change, reducing unnecessary rebuilds.
Isolated Declarations vs Traditional Type Checking: Performance Reality Check
Isolated declarations optimize declaration emit time, not type checking time. This distinction is critical. Teams that expect faster type checking after enabling the flag will be disappointed — type checking speed is unchanged.
%% alt: Performance comparison between traditional tsc pipeline and isolated declarations pipeline
flowchart LR
subgraph Traditional["Traditional: sequential bottleneck"]
TradBuild["Build: 50ms"]
TradDecl["tsc --declaration: 3-8s"]
TradCheck["Type check: 5s (separate)"]
TradBuild --> TradDecl
end
subgraph Isolated["Isolated: parallel optimized"]
IsoBuild["Build + declarations: 80ms"]
IsoCheck["Type check: 5s (separate)"]
end
style TradDecl stroke:#ef4444,fill:#450a0a,color:#fca5a5
classDef framework fill:#0b3b2e,stroke:#34d399,color:#d1fae5
classDef userAction fill:#142544,stroke:#7c9cf0,color:#eaf2ff
class TradBuild framework
class TradDecl framework
class IsoBuild framework
class TradCheck userAction
class IsoCheck userAction
The diagram shows both pipelines run type checking as a separate 5-second operation. The difference is declaration emit: 3-8 seconds reduced to 80ms. Type checking remains a CI concern, not a build concern.
Development workflows benefit immediately. Developers run the build command dozens of times per session. A 3-second wait after every save becomes a 80ms wait. Over an 8-hour day, this saves 30-45 minutes of idle time. Type checking runs once in CI before merge, where the 5-second duration is acceptable.
The implication here is that isolated declarations solve a different problem than developers expect. It does not make TypeScript faster at finding bugs. It makes your edit-refresh cycle faster by removing unnecessary work from the build step.
Migration Path: Fixing Your Code for isolatedDeclarations
Enabling isolatedDeclarations: true will break code that relies on type inference for public APIs. The compiler enforces explicit annotations because bundlers cannot infer types across module boundaries.
%% alt: Migration workflow for fixing isolated declarations errors
flowchart TD
Enable["Enable isolatedDeclarations flag"]
Build["Run build: tsc --noEmit"]
Errors["Compiler shows missing annotations"]
Fix["Add explicit return types"]
Verify["Verify declarations unchanged"]
Enable --> Build
Build --> Errors
Errors --> Fix
Fix --> Verify
Verify --> Build
classDef userAction fill:#142544,stroke:#7c9cf0,color:#eaf2ff
classDef framework fill:#0b3b2e,stroke:#34d399,color:#d1fae5
class Enable userAction
class Fix userAction
class Build framework
class Errors dataStore
The most common error is missing return types on exported functions. This fails:
// ❌ Fails with isolatedDeclarations
export function processUser(data: UserInput) {
return {
id: data.id,
name: data.name.toUpperCase(),
createdAt: new Date()
};
}The compiler cannot emit a declaration without analyzing the function body. The fix requires an explicit return type:
// ✅ Works with isolatedDeclarations
export interface ProcessedUser {
id: string;
name: string;
createdAt: Date;
}
export function processUser(data: UserInput): ProcessedUser {
return {
id: data.id,
name: data.name.toUpperCase(),
createdAt: new Date()
};
}The second common error is inferred parameter types in callbacks. Arrow functions passed as arguments must have explicit types:
// ❌ Fails with isolatedDeclarations
export const userHandlers = {
onCreate: (user) => console.log(user.name)
};
// ✅ Works with isolatedDeclarations
export const userHandlers = {
onCreate: (user: User): void => console.log(user.name)
};Teams migrating large codebases should enable the flag in tsconfig.json, run tsc --noEmit, and fix errors incrementally. The compiler reports every location that needs an annotation. Most codebases require 50-200 annotations for every 10,000 lines of code.
Tool Integration: esbuild, Rollup, and Vite with Isolated Declarations
Fast bundlers gain declaration emit support through plugins. Each tool requires configuration to enable the feature.
%% alt: Build tool integration workflow for isolated declarations
flowchart TD
Source["Source: .ts with explicit types"]
ESBuild["esbuild --declaration"]
Rollup["rollup-plugin-dts"]
Vite["vite-plugin-dts"]
Output["Output: .d.ts files"]
Source --> ESBuild
Source --> Rollup
Source --> Vite
ESBuild --> Output
Rollup --> Output
Vite --> Output
classDef framework fill:#0b3b2e,stroke:#34d399,color:#d1fae5
classDef dataStore fill:#3a2f0b,stroke:#fbbf24,color:#fef3c7
class ESBuild framework
class Rollup framework
class Vite framework
class Output dataStore
For esbuild, the official plugin supports isolated declarations in version 0.20+:
// esbuild.config.js
import { build } from 'esbuild';
await build({
entryPoints: ['src/index.ts'],
bundle: true,
outdir: 'dist',
declaration: true, // Requires isolatedDeclarations: true in tsconfig
format: 'esm'
});Rollup requires the rollup-plugin-dts plugin configured to use isolated mode:
// rollup.config.js
import dts from 'rollup-plugin-dts';
export default {
input: 'src/index.ts',
output: { file: 'dist/index.d.ts', format: 'es' },
plugins: [
dts({
respectExternal: true,
isolatedDeclarations: true
})
]
};Vite users configure vite-plugin-dts in their config:
// vite.config.ts
import { defineConfig } from 'vite';
import dts from 'vite-plugin-dts';
export default defineConfig({
plugins: [
dts({
insertTypesEntry: true,
isolatedDeclarations: true
})
]
});
All three tools emit declarations in parallel with JavaScript output. The build step completes in one pass instead of two sequential operations. Monorepo packages can build concurrently without waiting for upstream declaration files.
When Isolated Declarations Don't Replace Type Checking (And Why That's OK)
Isolated declarations optimize build pipelines, not validation pipelines. The feature does not check type safety. It assumes your code is already correct and emits declarations based on explicit annotations.
%% alt: Separation of concerns between build and validation pipelines
flowchart TD
Code["Developer writes code"]
Build["Build: isolated declarations emit"]
Deploy["Deploy: .js + .d.ts"]
CI["CI: tsc --noEmit checks types"]
Block["Block merge if types invalid"]
Code --> Build
Build --> Deploy
Code --> CI
CI --> Block
Block -.->|prevents| Deploy
style Block stroke:#ef4444,fill:#450a0a,color:#fca5a5
classDef userAction fill:#142544,stroke:#7c9cf0,color:#eaf2ff
classDef framework fill:#0b3b2e,stroke:#34d399,color:#d1fae5
class Code userAction
class Build framework
class CI framework
This separation matters in production pipelines. Development builds run fast because they skip type checking. CI runs slow because it performs exhaustive validation. Both concerns are necessary, but they serve different purposes at different times.
The failure mode here is subtle but expensive. Teams that disable CI type checking after enabling isolated declarations will ship runtime errors. The build succeeds because declarations emit without validation. The type errors remain in the codebase, undetected until production.
The correct approach separates the two concerns explicitly:
- Development build:
esbuild --declaration(fast iteration) - CI validation:
tsc --noEmit(comprehensive checking) - Production build: Same as development build (already validated)
This pattern enables fast local development while maintaining strict type safety in CI. Developers iterate quickly. CI catches errors before merge. Production receives validated, optimized builds.
Related patterns for monorepo builds appear in our parallel declaration emit guide and monorepo performance analysis.
Frequently Asked Questions
Does isolated declarations make TypeScript type checking faster?
No. Isolated declarations optimizes declaration file generation, not type checking. Type checking speed remains unchanged. The feature enables bundlers to emit .d.ts files without invoking the type checker, which accelerates build pipelines but does not affect the time required to validate type safety.
Can I use isolated declarations without changing my code?
No. Enabling isolatedDeclarations: true requires explicit type annotations on all exported functions, variables, and class members. Code that relies on cross-file type inference will fail compilation. Teams must add return types and parameter types before the feature works.
Do I still need tsc in my build pipeline with isolated declarations?
Yes, for type checking. You remove tsc --declaration from the build step, but tsc --noEmit remains necessary in CI to validate type safety. The build pipeline uses your bundler for speed; the validation pipeline uses tsc for correctness.
Which bundlers support isolated declarations?
esbuild 0.20+, Rollup with rollup-plugin-dts, and Vite with vite-plugin-dts all support isolated declarations. The feature is a bundler-agnostic TypeScript compiler flag, so any tool that emits declarations can adopt support through plugins or built-in functionality.
How much faster are builds with isolated declarations in a monorepo?
Declaration emit time drops from 3-8 seconds per package to 50-100ms per package. In a 20-package monorepo building in parallel, total build time typically decreases from 2+ minutes to 15-30 seconds. The exact improvement depends on dependency depth and package size.
Real-World Build Pipeline Transformation
That covers the essential patterns for integrating isolated declarations into production build pipelines. Enable the flag, fix the annotations, configure your bundler, and maintain separate CI type checking. The build time reduction is immediate and compounds across every package in your monorepo. Development velocity improves measurably while type safety guarantees remain intact.
For teams running tsc --declaration in dozens of packages, this change removes the slowest bottleneck in the entire build graph. Apply these patterns in production and the difference will be immediate.