Menulis TypeScript hanyalah setengah perjalanan; berkas .ts harus sampai ke CPU sebagai JavaScript yang berjalan. Ada dua jalan untuk itu, dan keduanya bukan pesaing melainkan pasangan: type stripping — Node.js membaca berkas .ts langsung, menghapus anotasi, lalu mengeksekusi sisanya — dan kompilasi — tsc menerjemahkan seluruh proyek menjadi berkas .js yang berdiri sendiri. Jalan pertama cepat dan tanpa langkah tambahan, jalan kedua kompat dengan lingkungan apa pun. Chapter ini menjalankan keduanya, memetakan batas tegasnya, lalu memberi resep kapan memakai yang mana.
Jalan Pertama: Penghapusan Tipe #
Sejak Node.js 22, runtime memahami berkas .ts secara bawaan: seluruh anotasi dihapus saat berkas dimuat, sisanya — JavaScript murni — dieksekusi seperti biasa. Mekanisme ini terdokumentasi resmi di halaman TypeScript pada referensi API Node.js
:
node salam.tsBerkas yang dijalankan berisi anotasi tipe, interface, dan generic — semuanya hilang tanpa jejak saat dieksekusi:
export function sapa(nama: string): string {
return `Halo, ${nama}!`;
}import { sapa } from "./salam.ts";
const pengguna = { nama: "Dewi", usia: 25 };
console.log(sapa(pengguna.nama));
console.log([1, 2, 3].map((n) => n * 2).join(", "));Output yang Dihasilkan:
Halo, Dewi!
2, 4, 6Tidak ada berkas perantara, tidak ada folder dist — anotasi hidup hanya di teks, dan program berjalan seketika. Perhatikan pula import specifier-nya: "./salam.ts" menuliskan ekstensi .ts apa adanya, dan Node me-resolusinya langsung. Inilah alasan konfigurasi pada Chapter Setup Proyek TypeScript mengaktifkan allowImportingTsExtensions — agar pemeriksa tipe menerima penulisan yang sama dengan yang dijalankan runtime.
Batas Tegas: Sintaks yang Tak Bisa Dihapus #
Penghapusan tipe hanya boleh membuang, tidak boleh menciptakan. Anotasi, interface, dan generic hilang tanpa meninggalkan apa pun; namun sintaks seperti enum tidak sekadar “hilang” — ia berperilaku pada runtime, menghasilkan objek sungguhan — sehingga tidak bisa dihapus begitu saja. Node menolaknya dengan galat khusus:
enum Warna {
Merah,
Hijau,
}
console.log(Warna.Merah, Warna.Hijau);try {
await import("./warna.ts");
console.log("berhasil dimuat");
} catch (e) {
console.log(`${e.name}: ${e.code}`);
}Output yang Dihasilkan:
SyntaxError: ERR_UNSUPPORTED_TYPESCRIPT_SYNTAXKode galatnya bermakna jelas: ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX — sintaks yang tidak didukung penghapusan tipe. Keluarganya pendek namun perlu dihafal: enum, namespace dengan kode di dalamnya, parameter properties pada constructor, dan decorator. Semuanya punya padanan yang bisa dihapus — enum digantikan union of object atau as const, parameter properties digantikan deklarasi field biasa — sehingga batasan ini jarang benar-benar menghalangi.
Jalan Kedua: Kompilasi #
Jalan kedua memakai kompiler penuh: tsc membaca seluruh proyek, memeriksa tipenya, lalu menuliskan berkas .js yang bisa berjalan di Node versi lama, di browser, atau di lingkungan yang tidak mengenal TypeScript sama sekali. Satu perbedaan penting menanti di penulisan import:
import { sapa } from "./salam.js";
console.log(sapa("Dewi"));Specifier kini menulis .js — padahal berkas di disk bernama sapa.ts. Ini bukan salah ketik melainkan aturan main: TypeScript mengasumsikan specifier merujuk hasil akhir berkas, dan sapa.js memang nama yang akan diberikan kompiler kepada sapa.ts. Mari kita jalankan kompilasinya:
npx --yes [email protected] --outDir dist --module nodenext --target esnext jalur-kompilasi.ts salam.ts
node dist/jalur-kompilasi.js
rm -rf distOutput yang Dihasilkan:
kode keluar: 0
Halo, Dewi!Urutannya: tsc menulis dist/jalur-kompilasi.js dan dist/salam.js, lalu Node menjalankan hasilnya tanpa tahu TypeScript pernah ada — specifier "./salam.js" kini menunjuk berkas yang sungguhan ada. Perintah rm -rf dist membersihkan hasil agar demonstrasi bisa diulang identik; pada proyek nyata folder dist justru dibuang ke .gitignore dan dibangun ulang oleh pipeline dari Chapter CI/CD dengan GitHub Actions.
Memilih Jalan dalam Praktik #
Kedua jalan itu membagi pekerjaan dengan rapi:
| Aspek | Penghapusan tipe | Kompilasi |
|---|---|---|
| Perintah | node berkas.ts | tsc lalu node dist/berkas.js |
| Kecepatan siklus | tanpa langkah tambahan | satu langkah build |
| Pemeriksaan tipe | tidak — perlu tsc --noEmit terpisah | termasuk dalam proses |
| Sintaks yang didukung | hanya yang bisa dihapus | seluruh bahasa |
| Target eksekusi | Node.js modern | runtime apa pun |
Kombinasi yang lazim untuk pengembangan lokal: tsc --noEmit untuk memeriksa tipe setiap kali menyimpan, node berkas.ts untuk menjalankan — persis duet yang dipakai seluruh chapter section ini. Untuk penyebaran, banyak tim memilih kompres keduanya: aplikasi dijalankan langsung di Node modern sementara package yang akan dikonsumsi orang lain dikompilasi agar konsumennya tidak diminta mengikuti. Batas keputusannya bukan selera, melainkan pertanyaan sederhana: siapa yang akan menjalankan berkasnya, dan Node versi berapa di sana.
Dengan dua jalan ini, TypeScript sudah bisa hidup penuh di proyek Node.js kita. Satu tantangan terakhir tetap ada: proyek yang sudah berjalan dengan ribuan baris JavaScript tidak bisa berhenti seminggu untuk migrasi. Bab penutup section ini menyusun strategi pindahnya selangkah demi selangkah tanpa menghentikan kereta yang sedang berjalan.