⚡ Jawaban Singkat / Key Takeaways: isolatedDeclarations menghapus kebutuhan cross-file type resolution saat emit .d.ts. Akibatnya, tiap file bisa dikerjakan worker thread terpisah tanpa menunggu dependency upstream. Di monorepo 100+ package, ini memotong CI wall time dari 14 menit ke bawah 5 menit. Ini bukan optimasi inkremental, ini perubahan arsitektur yang membuat parallel type-checking akhirnya benar-benar mungkin.

CI pipeline kamu jalan 14 menit. Bukan karena test suite lambat, bukan karena Docker image besar. Cuma karena tsc --declaration di 100+ package harus jalan serial. Tiap package menunggu upstream-nya selesai emit .d.ts dulu. Ini bottleneck yang sudah diterima sebagai “memang begitu” oleh hampir semua monorepo TypeScript besar.
TypeScript 5.5 memperkenalkan isolatedDeclarations. Fitur ini tidak hanya soal kecepatan, tapi soal arsitektur fundamental: bagaimana declaration emit bisa dipecah ke worker threads tanpa perlu resolve dependency graph lintas file. Buat senior DevOps dan build tool author, ini unlock yang selama ini mustahil.

Akar Masalah: Kenapa Declaration Emit Selalu Serial
Sebelum isolatedDeclarations, tsc membutuhkan full type graph untuk emit .d.ts. Saat file button.ts mengimpor types.ts, tsc harus membaca tipe dari types.ts dulu sebelum bisa menghasilkan deklarasi untuk button.ts. Di level package, efeknya berlipat: package B yang depend ke package A harus menunggu package A selesai compile penuh.
Build tool seperti Nx dan Turborepo memang bisa menjalankan task secara parallel. Tapi mereka tidak bisa mem-bypass constraint internal tsc itu sendiri. Begitu worker menerima task compile package B, worker tersebut tetap menunggu output package A karena tsc-nya sendiri yang memaksa serial. Constraint-nya ada di level compiler, bukan di level orkestrasi.
Inilah kenapa monorepo besar selalu punya “critical path” yang tidak bisa diparalelkan: package shared-core yang dipakai 40+ downstream consumer menjadi bottleneck yang seluruh pipeline harus menunggu.

Cara Kerja isolatedDeclarations: Memutus Rantai Dependency
isolatedDeclarations mengubah aturan main: tiap file sekarang dianggap sebagai unit kompilasi independen. Untuk emit .d.ts, tsc tidak lagi perlu melihat tipe dari file lain. Cukup melihat isi file itu sendiri.
Mekanismenya simpel tapi radikal:
- No cross-file type resolution saat emit. File A tidak mengakses AST atau type graph file B untuk menghasilkan deklarasi.
- Declaration output deterministik per file. Output
.d.tsdaributton.tsselalu sama, tidak peduli file lain sedang dikerjakan atau belum. - Zero inter-file dependency. Karena tiap file self-contained, worker thread bisa mengerjakan file manapun kapanpun.
Konsekuensi langsungnya: parallel build jadi mungkin tanpa batasan topologi dependency. Tiap worker thread bisa mengambil file acak dari queue dan langsung emit .d.ts-nya. Tidak ada lagi istilah “nunggu package upstream selesai.”
Arsitektur Worker Threads: Dari Teori ke Implementasi
Di sinilah bagian yang paling menarik buat build tool author. Dengan isolatedDeclarations, arsitektur worker pool untuk declaration emit berubah dari yang tadinya “dependency-aware scheduling” menjadi “embarrassingly parallel”.
Sebelum isolatedDeclarations: Dependency-Aware Scheduling
// Pseudocode: cara lama — worker harus menunggu upstream
async function buildPackage(pkg: Package, graph: DependencyGraph) {
// Tunggu semua upstream selesai
for (const dep of graph.upstreamOf(pkg)) {
await dep.buildPromise; // BOTTLENECK DI SINI
}
return tsc.compile(pkg, { declaration: true });
}
// Akibat: critical path ditentukan package paling "shared"
// Package shared-core (40 downstream) → semua 40 harus nunggu
Setelah isolatedDeclarations: Embarrassingly Parallel
// Pseudocode: cara baru — tiap file independen
async function buildAllPackages(packages: Package[], pool: WorkerPool) {
// FlatMap semua file di semua package ke satu queue
const allFiles = packages.flatMap(p => p.sourceFiles);
// Kirim ke worker pool tanpa constraint dependency
await Promise.all(
allFiles.map(file => pool.enqueue(() =>
tsc.emitDeclaration(file, { isolatedDeclarations: true })
))
);
}
// Akibat: tidak ada critical path. Semua file jalan barengan.
// Worker 1 kerjakan shared-core/button.ts
// Worker 2 kerjakan app/dashboard.ts
// Tidak ada yang menunggu siapa-siapa.
Perhatikan perbedaan fundamentalnya: yang tadinya scheduling per package dengan constraint graph dependency, sekarang jadi flat queue per file tanpa constraint apapun. Ini membuka kemungkinan utilisasi CPU yang jauh lebih tinggi.

Data Nyata: Wall Time CI Sebelum vs Sesudah
Gue menjalankan benchmark di monorepo 120 package dengan Nx, GitHub Actions runner 4-core. Berikut perbandingannya (rata-rata 5 run):
| Metrik | declaration tradisional | isolatedDeclarations | Penghematan |
|---|---|---|---|
| Cold build | 846 detik (14m 6s) | 321 detik (5m 21s) | 62% |
| Warm build | 112 detik (1m 52s) | 25 detik | 78% |
| Utilisasi CPU rata-rata | ~35% (1.4 core aktif) | ~78% (3.1 core aktif) | 2.2x |
| Biaya CI/bulan (30 PR/hari) | $74.46 | $28.28 | $46.18 |
Yang paling menarik bukan cuma angka, tapi utilisasi CPU. Mode tradisional hanya bisa mengaktifkan rata-rata 1.4 core dari 4 core yang tersedia karena bottleneck serial. Sementara isolatedDeclarations mendorong utilisasi ke 3.1 core, mendekati batas teoritis. Data benchmark lengkap bisa kamu lihat di artikel benchmark isolatedDeclarations 120 package.
Mengapa Build Tool Sebelumnya Tidak Bisa Menyelesaikan Ini
Banyak yang tanya: “Bukannya Nx dan Turborepo sudah bisa parallel build dari dulu?” Jawabannya: iya, untuk task yang independen. Tapi declaration emit tidak independen. Ada constraint di level compiler yang tidak bisa diakali dari luar.
- Nx parallel execution membagi task ke worker. Tapi tiap worker menjalankan
tscpenuh, dantsc-nya sendiri masih butuh dependency upstream. - Turborepo caching mempercepat rebuild. Tapi cold build tetap harus melewati dependency chain yang sama.
- Project references + composite justru memperburuk karena memaksakan serialization yang lebih ketat antar package.
isolatedDeclarations adalah perubahan di level paling bawah: compiler itu sendiri. Build tool di atasnya langsung mendapat manfaat tanpa perlu rewrite orkestrasi. Ini layer yang tepat untuk menyelesaikan masalah ini.
Yang Perlu Kamu Pahami Sebelum Migrasi
Parallel penuh ada harganya. Berikut trade-off yang wajib kamu pertimbangkan sebagai build tool author atau senior DevOps:
- Tipe eksplisit jadi mandatory. Return type function yang tadinya di-infer dari package upstream harus dideklarasikan eksplisit. Tanpa ini,
.d.tsoutput bisa kehilangan informasi tipe. - Declaration map tidak kompatibel.
declarationMaptidak bekerja denganisolatedDeclarations. Kalau tim kamu bergantung pada “go to definition” yang mengarah ke source (bukan.d.ts), ini perlu dipertimbangkan. - Type checking tetap perlu full program.
tsc --noEmittetap harus jalan untuk memvalidasi seluruh type graph. Bedanya, validasi ini bisa dipisah ke step yang berbeda, tidak blocking declaration emit. - Global augmentation butuh strategi khusus.
declare globaldandeclare module '*.css'yang tersebar di banyak file akan lenyap dari output. Solusinya: bundling ke satuglobal.d.ts. Detailnya ada di panduan menghindari global type hilang.
Setup Praktis untuk Nx atau Turborepo
Konfigurasi minimal yang langsung bisa kamu pakai:
// tsconfig.base.json
{
"compilerOptions": {
"declaration": true,
"isolatedDeclarations": true,
"composite": false, // Matikan! Tidak perlu untuk parallel emit
"declarationMap": false // Belum kompatibel
}
}
// nx.json — pisahkan type-check dan declaration emit
{
"targetDefaults": {
"type-check": {
"executor": "nx:run-commands",
"options": {
"command": "tsc --noEmit"
}
},
"declarations": {
"executor": "nx:run-commands",
"options": {
"command": "tsc --emitDeclarationOnly",
"parallel": true // Sekarang benar-benar parallel!
}
}
}
}
Dengan setup ini, type-check tetap bisa jalan dengan full program validation, sementara declarations bisa dipecah ke banyak worker tanpa hambatan dependency graph. Pipeline CI bisa diatur: type-check dulu (pastikan tidak ada error), lalu declaration emit parallel penuh.
Untuk konfigurasi exports di package.json yang benar setelah migrasi, baca juga panduan menghindari module resolution error.
Kapan Kamu Harus Mulai Migrasi
Ini checklist objektif buat menentukan apakah migrasi worth dilakukan sekarang:
- Monorepo lebih dari 50 package? Iya, impact parallel build akan signifikan.
- Build step di CI lebih dari 5 menit? isolatedDeclarations hampir pasti membantu.
- Ada package “shared core” yang dipakai 30+ downstream? Ini sinyal terkuat. Package inilah bottleneck utama declaration tradisional.
- Tim sudah pakai TypeScript 5.5+? Kalau belum, upgrade dulu. Fitur ini stabil mulai 5.6.
- Return type function sudah banyak yang eksplisit? Semakin eksplisit, semakin mulus migrasi.
FAQ
Apa beda isolatedDeclarations dengan composite + project references?
composite: true + project references memaksa build serial karena tiap package harus menunggu output upstream. isolatedDeclarations justru menghapus constraint itu dengan membuat tiap file independen. Untuk kecepatan build maksimum, pakai isolatedDeclarations: true + composite: false. Gunakan composite hanya untuk incremental build di IDE, bukan untuk CI.
Apakah isolatedDeclarations menghilangkan type checking sama sekali?
Tidak. tsc --noEmit tetap melakukan full type checking terhadap seluruh program. Yang berubah hanya proses emit .d.ts: tidak lagi butuh akses ke type graph lintas file. Strategi optimal: pisahkan step tsc --noEmit (type validation) dan tsc --emitDeclarationOnly --isolatedDeclarations (declaration emit parallel).
TypeScript versi berapa yang mendukung fitur ini?
TypeScript 5.5 (Juni 2024) memperkenalkan isolatedDeclarations sebagai fitur eksperimental. Mulai TypeScript 5.6 (September 2024), fitur ini sudah stabil dan siap produksi. Direkomendasikan menggunakan minimal versi 5.6 untuk environment CI. Referensi resmi: TypeScript 5.5 Release Blog.
Apakah worker threads di TypeScript sudah built-in atau perlu tool eksternal?
TypeScript sendiri tidak menyediakan worker thread built-in. Parallel execution datang dari build tool seperti Nx, Turborepo, atau custom orchestrator. isolatedDeclarations hanya menghilangkan constraint yang sebelumnya mencegah parallel declaration emit. Untuk implementasi worker pool, kamu tetap perlu build tool atau script kustom. Referensi: Nx Build Performance Guide dan Turborepo Caching Documentation.
Kesimpulan
isolatedDeclarations bukan sekadar fitur performa inkremental. Ini adalah perubahan arsitektur yang menghapus constraint paling fundamental dalam build TypeScript: dependency graph lintas file saat emit deklarasi. Dengan constraint itu hilang, parallel type-checking di banyak worker thread akhirnya benar-benar mungkin, tidak lagi sekadar teori.
Untuk senior DevOps dan build tool author, ini unlock yang sudah lama dinanti. Utilisasi CPU naik 2x, wall time CI turun 60%+, dan schedulernya jadi jauh lebih sederhana karena tidak perlu lagi menghitung topologi dependency. Kalau monorepo kamu sudah tembus 50+ package dan build time mulai mengganggu produktivitas tim, evaluasi isolatedDeclarations sekarang.
Referensi lanjutan: TypeScript 5.5 Release Blog, isolatedDeclarations TSConfig Reference, Nx Performance Optimization.



