⚡ Jawaban Singkat / Key Takeaways: TypeScript mengelide import type dari output JavaScript saat runtime. Tapi tanpa isolatedDeclarations, proses emit .d.ts bisa menyentuh modul upstream dan memicu eksekusi top-level code yang tidak kamu sadari. Ini celah serius: prototype pollution, side effect berbahaya, bahkan code execution dari third-party declarations yang seharusnya cuma konsumsi tipe. isolatedDeclarations menutup celah ini dengan memastikan tiap file benar-benar terisolasi, tanpa akses ke type graph modul lain.
Kamu Impor Tipe, Tapi Kode Tetap Jalan
Sebuah library kecil bernama safe-utils muncul di npm. 40 ribu download per minggu. Typenya lengkap, dokumentasinya rapi. Kamu impor cuma tipenya: import type { Validator } from 'safe-utils'. Aman, kan? TypeScript mengelide import type dari output JavaScript. Tidak ada kode safe-utils yang ikut ke bundle production-mu.
Tapi ada satu detail yang luput: proses emit .d.ts di library itu sendiri. Waktu library author menjalankan tsc --declaration tradisional, compiler menyentuh type graph semua modul yang diimpor, termasuk modul dengan top-level side effect. Di sinilah celah bermain. Sebuah .d.ts yang terlihat innocent bisa memicu eksekusi kode yang tidak pernah kamu duga.
Ini bukan skenario fiksi. Ekosistem npm sudah berulang kali kena kasus typosquatting dan dependency confusion di mana package declaration berperilaku tidak terduga. isolatedDeclarations adalah perisai yang selama ini tidak kamu sadari keberadaannya.
Bagaimana Type-Only Import Bisa Bocor
Untuk memahami celah ini, kamu harus paham dulu dua mekanisme TypeScript yang bertabrakan.
Import Type: Dielide, Tapi Tidak Sepenuhnya Mati
import type dihapus dari output JavaScript. Ini fitur keamanan fundamental TypeScript. Kode berikut:
// library.ts
import type { Validator } from 'safe-utils';
export function validate(input: string): Validator { ... }
Menghasilkan JS yang bersih, tanpa jejak safe-utils:
// library.js (output)
export function validate(input) { ... }
Tapi ini baru sisi consumer. Di sisi publisher, situasi berbeda. Waktu library author emit .d.ts dengan mode declaration: true tradisional, compiler membaca type graph modul upstream untuk menghasilkan deklarasi yang akurat. Proses ini bisa menyentuh file modul yang memiliki top-level side effect.
Top-Level Side Effect: Bom Waktu di Declaration Emit
Pertimbangkan skenario ini. Sebuah package naive-helper punya file entry:
// naive-helper/src/index.ts
import './polyfills'; // Top-level side effect!
export type { Helper } from './types';
Dan file polyfills.ts-nya:
// naive-helper/src/polyfills.ts
Object.prototype.isEmpty = function() {
return Object.keys(this).length === 0;
};
Ini prototype pollution klasik. Modul pollyfills memodifikasi Object.prototype secara global. Sekarang bayangkan library kamu mengimpor tipe Helper dari package ini. Dengan declaration: true tradisional, tsc mungkin perlu membaca file entry naive-helper untuk resolve tipe Helper. Proses resolve ini menyentuh file index.ts, yang otomatis mengeksekusi import './polyfills'. Pollution terjadi, aplikasi-mu terpengaruh, dan kamu bahkan tidak sadar karena kamu cuma import type.
isolatedDeclarations: Isolasi Total Tanpa Sentuh Modul Lain
Di sinilah isolatedDeclarations berperan. Fitur ini memaksa tiap file dikompilasi sebagai unit independen total. Untuk emit .d.ts, compiler tidak menyentuh type graph modul upstream. Cukup melihat isi file itu sendiri.
Efek keamanannya langsung dan fundamental:
- Zero cross-file resolution. File A tidak bisa mengakses AST atau type graph file B. Tidak ada alasan untuk runtime loading modul upstream.
- No accidental module execution. Karena compiler tidak perlu resolve tipe dari package lain, modul dengan top-level side effect tidak pernah disentuh.
- Declaration output deterministik. Output
.d.tsdari sebuah file selalu identik, tidak peduli modul lain apa yang terinstal dinode_modules.
Ini bukan sekadar optimasi build. Ini adalah security boundary yang memisahkan proses kompilasi deklarasi dari ekosistem runtime package lain.
Apa Kata Spesifikasi: Import Elision di ECMAScript dan TypeScript
Spesifikasi ECMAScript tidak mengenal import type. Fitur ini murni dari TypeScript. Dokumentasi TypeScript menyatakan bahwa import type akan dihapus sepenuhnya dari output. Tapi penting dipahami: penghapusan ini terjadi di level emit JavaScript, bukan di level type checking.
Mode declaration: true tradisional melanggar prinsip ini secara implisit. Untuk menghasilkan .d.ts yang akurat, compiler mungkin perlu menyentuh modul yang diimpor. isolatedDeclarations memperbaiki inkonsistensi ini dengan menerapkan isolasi di kedua level: emit runtime dan emit deklarasi.
Referensi resmi: TypeScript 5.5 Release Blog dan isolatedDeclarations TSConfig Documentation.
Vektor Serangan yang Ditutup
Berikut tiga vektor spesifik yang dinetralkan oleh isolatedDeclarations:
1. Prototype Pollution via Declaration Resolve
Package types-helpers punya file setup.ts yang memodifikasi Array.prototype. Library kamu cuma impor tipenya. Tapi dengan declaration: true tradisional, tsc menyentuh setup.ts saat resolve tipe. Prototype tercemar sebelum aplikasi kamu bahkan mulai.
isolatedDeclarations memotong akses ke setup.ts sepenuhnya. File itu tidak pernah disentuh compiler.
2. Supply Chain Side Effect via Types-Only Dependency
Package @types/helper mendeklarasikan tipe yang mengimpor modul runtime. Ini umum terjadi di DefinitelyTyped. Dengan mode tradisional, tsc bisa memuat modul runtime untuk memvalidasi deklarasi. Ini berpotensi mengeksekusi kode dari package yang seharusnya hanya menyediakan tipe.
isolatedDeclarations menghilangkan kebutuhan validasi lintas modul. Tiap file diverifikasi secara independen.
3. Dynamic Import Trigger via Conditional Type Resolution
Beberapa library menggunakan typeof import() di deklarasi conditional. Saat tsc mencoba resolve tipe conditional ini, ia bisa memicu dynamic import() yang mengeksekusi top-level code. isolatedDeclarations tidak mengizinkan conditional type resolution lintas file, menutup vektor ini sepenuhnya.
Apa yang Berubah untuk Security Auditor
Kalau pekerjaanmu mengaudit keamanan library dan supply chain, isolatedDeclarations memberi sinyal positif yang kuat. Library yang sudah mengadopsi fitur ini menunjukkan bahwa author mereka sadar akan batas keamanan antara type checking dan runtime execution.
Berikut checklist audit yang bisa kamu gunakan:
- Periksa
tsconfig.jsonlibrary. ApakahisolatedDeclarations: truesudah diaktifkan? Kalau belum, tanyakan kenapa. - Audit file
.d.tsoutput. Apakah ada jejak modul upstream yang seharusnya tidak muncul? Import yang tidak perlu dalam deklarasi adalah indikator potensi kebocoran. - Jalankan
npx attw(Are The Types Wrong) untuk validasi type resolution. Library yang bersih seharusnya tidak punya masalah resolusi tipe. - Periksa dependensi
@types/*. Package ini seharusnya hanya menyediakan tipe. Kalau ada modul runtime di dalamnya, itu bendera merah. - Test dengan
skipLibCheck: true. Banyak project consumer pakai ini. Kalau library bermasalah di mode ini, ada yang tidak beres dengan isolasi tipenya.
Adopsi Praktis untuk Library Author
Mengaktifkan isolatedDeclarations sederhana. Tapi ada beberapa aturan yang perlu kamu ikuti:
// tsconfig.json
{
"compilerOptions": {
"declaration": true,
"isolatedDeclarations": true,
"composite": false,
"declarationMap": false
}
}
- Return type eksplisit wajib. Function yang tadinya infer tipe dari modul upstream harus ditulis manual. Tanpa ini, output
.d.tsbisa kehilangan informasi tipe. compositeharus dimatikan. Composite memaksa project references yang justru membuka kembali akses lintas modul.- Global augmentation harus dibundel. Simak panduan mencegah global type hilang di isolatedDeclarations untuk detailnya.
- Konfigurasi
exportsdipackage.jsonharus eksplisit. Baca juga panduan exports package.json biar consumer tidak kena module resolution error.
Untuk benchmark performa build setelah adopsi, cek benchmark isolatedDeclarations di 120 package monorepo. Benefit keamanan datang bareng peningkatan kecepatan build yang signifikan.
FAQ
Apakah import type selalu aman dari eksekusi runtime?
Di level output JavaScript, iya. import type selalu dihapus dari bundle. Tapi di level proses kompilasi deklarasi, mode declaration: true tradisional bisa menyentuh modul upstream untuk resolve tipe. Ini berpotensi mengeksekusi top-level code modul tersebut. isolatedDeclarations menghilangkan potensi ini dengan memastikan tiap file dikompilasi independen, tanpa akses ke modul lain. Jadi jawaban aman: pakai import type dan isolatedDeclarations.
Apakah isolatedDeclarations melindungi dari malicious package di node_modules?
Tidak sepenuhnya. isolatedDeclarations hanya melindungi dari eksekusi yang dipicu oleh proses emit deklarasi. Kalau package jahat sudah terinstal dan aplikasi kamu mengimpornya secara runtime (bukan type-only), tidak ada yang bisa melindungi. Fitur ini spesifik mencegah kebocoran melalui jalur type checking dan declaration emit. Selalu kombinasikan dengan npm audit, dependency review, dan kebijakan trustedDependencies.
Apakah consumer library perlu mengaktifkan isolatedDeclarations juga?
Tidak wajib, tapi sangat disarankan. Kalau library yang kamu konsumsi sudah mengadopsi isolatedDeclarations, output .d.ts-nya sudah bersih dari kebocoran. Tapi project-mu sendiri mungkin punya dependensi lain yang belum mengadopsi. Mengaktifkan isolatedDeclarations di project consumer memberi perlindungan tambahan: tiap file di project-mu juga diisolasi, mencegah potensi kebocoran dari arah manapun.
Bagaimana cara memverifikasi library sudah aman dari prototype pollution via type import?
Langkah paling praktis: cek tsconfig.json library. Cari isolatedDeclarations: true. Lalu install library tersebut di project kosong, jalankan tsc --noEmit, dan periksa apakah ada warning atau error yang tidak terduga. Gunakan juga npx attw untuk validasi type resolution. Dokumentasi lengkap: Are The Types Wrong.
Kesimpulan
import type saja tidak cukup. Di balik janji elision runtime, proses declaration emit tradisional menyimpan celah yang jarang dibahas: akses ke type graph modul upstream bisa memicu eksekusi top-level code, membuka pintu prototype pollution dan side effect yang tidak kamu inginkan.
isolatedDeclarations menutup celah ini dari akar. Dengan memberlakukan isolasi total per file, fitur ini memastikan bahwa konsumsi tipe dari third-party package benar-benar bersih. Tidak ada modul upstream yang disentuh, tidak ada top-level code yang tereksekusi, tidak ada prototype yang tercemar diam-diam.
Untuk security auditor, ini adalah sinyal positif yang harus masuk checklist audit. Untuk library consumer, ini adalah lapisan keamanan tambahan yang bisa kamu aktifkan hari ini. Untuk library author, ini adalah tanggung jawab yang menunjukkan bahwa kamu peduli pada keamanan ekosistem.
Referensi lebih lanjut: TypeScript 5.5 Release Blog, TSConfig isolatedDeclarations Reference, Are The Types Wrong.
