In modern enterprise development and large-scale monorepos (Turborepo, Nx, Lerna), JavaScript transpilation has long ceased to be the primary bottleneck. Next-generation native tools built in Rust and Go (SWC, esbuild, Oxc, Rolldown) transform millions of lines of code dozens of times faster than the classic single-threaded tsc compiler.
However, a critical bottleneck persisted during library publishing and package compilation: generating type declaration files (.d.ts).
Historically, single-file transpilers could not emit .d.ts files. If an exported function or constant lacks an explicit type annotation, the compiler must inspect the implementations of all upstream dependencies:
// To generate .d.ts for this file:
import { getUser } from './user-service';
export const currentUser = getUser(); // What is the type of currentUser?To resolve the type of currentUser, tsc must construct a global semantic type graph across the entire project, navigate into user-service.ts, and parse its implementation and import chain. Single-file Rust/Go transpilers do not inspect neighbor files across the filesystem or run global type checks. As a result, the type-generation stage in CI/CD pipelines hit an unavoidable bottleneck in slow tsc --declaration.
To solve this fundamental challenge, TypeScript 5.5+ introduced the compiler flag --isolatedDeclarations.
The --isolatedDeclarations flag enforces explicit typing at the public boundary of modules: every file can emit its .d.ts completely autonomously. This unlocks type declaration generation by native multithreaded compilers (Oxc, SWC, Rolldown) with a 30–50x speedup in CI/CD pipelines.
The isolatedDeclarations architecture establishes a strict separation of architectural concerns:
If the compiler encounters export function calculate(a: number, b: number) { return a + b; }, --isolatedDeclarations mode emits error TS9007: "Exported function must have an explicit return type".
With this guarantee in place, a native generator (such as Oxc in Rust) no longer parses function bodies: it inspects only the signature headers (export function calculate(a: number, b: number): number) and instantly flushes the .d.ts file to disk.
+-------------------------------------------------------------------------+
| Evolution of Type Declaration File (.d.ts) Generation |
+-------------------------------------------------------------------------+
| Traditional pipeline (tsc --declaration): |
| [File A] ──┐ |
| [File B] ──┼──> [tsc Global Type Checker] ──> [.d.ts files] |
| [File C] ──┘ • Single-threaded, slow (Node.js runtime) |
| • Requires building type graph of ALL dependencies |
+-------------------------------------------------------------------------+
│
▼ --isolatedDeclarations flag (TS 5.5+)
+-------------------------------------------------------------------------+
| Parallel native pipeline (Oxc / Rolldown / SWC): |
| [File A (Explicit types)] ──> [Rust Thread 1] ──> [File A.d.ts] |
| [File B (Explicit types)] ──> [Rust Thread 2] ──> [File B.d.ts] |
| [File C (Explicit types)] ──> [Rust Thread 3] ──> [File C.d.ts] |
| • Single-file independent emit |
| • 30–50x speedup in type emission across multicore CPUs |
+-------------------------------------------------------------------------+Add 'isolatedDeclarations': true and 'declaration': true to the compilerOptions block.
Run tsc --noEmit and annotate missing return types on exported functions, methods, and constants.
Ensure that all interfaces and DTOs referenced in public signatures carry the export keyword.
Replace tsc --declaration with parallel .d.ts emit via Oxc or Rolldown for near-instant package builds.
Why does enabling the isolatedDeclarations flag disallow exporting 'export const config = { retries: 3, timeout: 5000 };' without an explicit type or as const?
In enterprise monorepos containing hundreds of packages, type generation via tsc often consumed 10–15+ minutes. Migrating packages to --isolatedDeclarations and running parallel Rust type emitters (Oxc/Rolldown) cuts this stage down to 10–20 seconds.
Mandating explicit annotations on exported functions protects libraries against unintended breaking changes: developers cannot accidentally alter a public return type by modifying internal if/else control flow branches.
Integrating Rolldown (the Rust-powered bundler) allows simultaneous compilation of JavaScript bundles and .d.ts declaration trees in a single hyper-fast pass without Node.js runtime overhead.
Linters such as oxlint automatically detect missing export type annotations and apply autofixes that insert computed types directly into source code.
Here is the code structure and configuration required to comply with isolatedDeclarations.
Enable 'isolatedDeclarations': true and 'declaration': true in tsconfig.json.
tsc or oxlint verifies explicit types across all public export statements.
The native Oxc parser reads only signature headers, dropping function implementations immediately.
OS threads synchronously flush .d.ts files to disk with minimal memory footprint.
{
"compilerOptions": {
"target": "ES2022",
"module": "NodeNext",
"moduleResolution": "NodeNext",
"declaration": true,
// Enable strict isolated declarations mode (TS 5.5+)
"isolatedDeclarations": true,
// Recommended alongside isolatedModules
"isolatedModules": true,
"strict": true,
"skipLibCheck": true
}
}export interface UserProfile {
readonly id: string;
readonly name: string;
readonly roles: readonly string[];
}
// ─────────────────────────────────────────────────────────────
// 1. Exporting functions: Explicit return type REQUIRED
// ─────────────────────────────────────────────────────────────
// ❌ ERROR TS9007 under isolatedDeclarations:
// export function fetchUser(id: string) {
// return { id, name: 'Alice', roles: ['admin'] };
// }
// ✅ CORRECT: Signature explicitly locks the contract
export function fetchUser(id: string): UserProfile {
return {
id,
name: 'Alice',
roles: ['admin'],
};
}
// ─────────────────────────────────────────────────────────────
// 2. Exporting constants: Explicit type annotation or as const REQUIRED
// ─────────────────────────────────────────────────────────────
export interface ClientConfig {
readonly timeoutMs: number;
readonly retryCount: number;
}
// ✅ Option A: Explicit interface type
export const DEFAULT_CONFIG: ClientConfig = {
timeoutMs: 5000,
retryCount: 3,
};
// ✅ Option B: Constant assertion with as const
export const HTTP_STATUS = {
OK: 200,
NOT_FOUND: 404,
INTERNAL_ERROR: 500,
} as const;
// ─────────────────────────────────────────────────────────────
// 3. Classes: Public members require explicit types
// ─────────────────────────────────────────────────────────────
export class ApiGateway {
// Public property requires an explicit type
public readonly endpoint: string;
constructor(endpoint: string) {
this.endpoint = endpoint;
}
// Public method requires an explicit return type
public getUrl(path: string): string {
return `${this.endpoint}${path}`;
}
// Private methods do NOT require annotations because they are omitted from .d.ts!
private calculateBackoff(attempt: number) {
return Math.pow(2, attempt) * 100;
}
}A function is declared as: 'interface InternalData { hash: string; } export function getHash(): InternalData { ... }'. Why does tsc with isolatedDeclarations trigger error TS9010?
Here is a monorepo package configuration integrating the Oxc Rust compiler for instant .d.ts declaration generation paired with decoupled verification via tsc --noEmit.
{
"name": "@agenticlab/core-engine",
"version": "1.0.0",
"type": "module",
"scripts": {
"typecheck": "tsc --noEmit",
"build:code": "rolldown -c rolldown.config.ts",
"build:types": "oxc-transform --emit-declarations src -d dist/types",
"build": "pnpm run typecheck && pnpm run build:code && pnpm run build:types"
},
"devDependencies": {
"oxc-transform": "^0.15.0",
"rolldown": "^0.14.0",
"typescript": "^5.5.0"
}
}If an exported function returns a local interface lacking the export keyword, a single-file generator cannot produce a valid .d.ts file because external consumers cannot resolve the type name.
// ❌ ERROR: Local interface is not exported!
interface SecretConfig { key: string; }
export function loadConfig(): SecretConfig { /* ... */ }
// Error TS9010: Variable must have an explicit type, but its type cannot be named.
// ✅ CORRECT: Export the interface alongside the function
export interface SecretConfig { key: string; }
export function loadConfig(): SecretConfig { /* ... */ }Emitters such as Oxc and SWC perform purely syntactic emission of .d.ts based on existing explicit annotations. They do not perform semantic type checking. Removing tsc --noEmit from CI/CD allows critical type mismatches and bugs to escape into production.
The --isolatedDeclarations flag applies strictly to the public module surface (export). Annotating every local variable inside function bodies or typing private class methods (private helper(): void) introduces visual noise without providing compiler benefits.
The --isolatedDeclarations flag in TypeScript 5.5+ fundamentally transforms build performance in large-scale codebases. By enforcing explicit typing at the public export boundary, it decouples .d.ts declaration generation from the slow single-threaded tsc process and empowers next-generation native Rust tools (Oxc, Rolldown, SWC), accelerating monorepo CI/CD pipelines by dozens of times.