Jalankan TypeScript Native di Node.js: Type Stripping

Foto oleh Markus Spiske on Pexels
Bisa. Sejak Node 22.6.0 (di balik --experimental-strip-types) dan secara default sejak Node 23.6.0 serta 22.18.0, Node menjalankan file .ts langsung memakai type stripping. Node menghapus anotasi tipe dan mengeksekusi sisa JavaScript-nya, jadi Anda tidak lagi butuh tsc, ts-node, atau bundler hanya untuk menjalankan sebuah file.
Tidak. Type stripping hanya menghapus anotasi tipe dan menggantinya dengan spasi; tidak ada type checking. Node akan menjalankan kode yang penuh error tipe tanpa protes. Anda tetap perlu menjalankan npx tsc --noEmit secara terpisah, biasanya sebagai tahap CI, untuk menangkap error tipe.
Type stripping hanya menangani sintaks yang bisa dihapus dan menyisakan JavaScript valid. Enum, namespace dengan kode runtime, parameter properties, dan import alias harus diganti oleh JavaScript baru, bukan dihapus, sehingga stripping biasa melempar ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX. Pakai --experimental-transform-types atau ubah ke bentuk yang bisa dihapus.
--experimental-strip-types (default) hanya menghapus anotasi tipe, mempertahankan posisi karakter agar stack trace tetap akurat. --experimental-transform-types benar-benar menulis ulang fitur yang tidak bisa dihapus seperti enum dan namespace menjadi JavaScript. Transform mode menggeser posisi, jadi padukan dengan --enable-source-maps.
Node memakai paket bernama amaro, yang membungkus build WebAssembly dari swc, toolchain TypeScript dan JavaScript berbasis Rust. swc melakukan kerja parse-dan-strip yang cepat, sementara amaro adalah lapisan yang dipakai Node untuk memanggilnya dan memperbarui versi TypeScript yang dibundel secara independen dari Node.

Foto oleh Markus Spiske on Pexels
Ringkasan Utama
Type stripping membuat Node.js menjalankan file TypeScript secara langsung dengan menghapus anotasi tipe dan menggantinya dengan spasi, sehingga tidak perlu langkah build tsc. Fitur ini tidak melakukan type checking. Diperkenalkan di balik --experimental-strip-types pada Node 22.6 dan berjalan tanpa flag sejak Node 23.6. Enum dan namespace butuh transform mode.
Selama bertahun-tahun jawaban jujur atas cara menjalankan TypeScript di server adalah: sebenarnya tidak bisa langsung. Anda menjalankan JavaScript yang dihasilkan langkah build dari TypeScript. Setiap proyek membawa pemanggilan tsc, atau ts-node, atau sebuah bundler, yang duduk di antara file yang Anda tulis dan proses yang benar-benar mengeksekusinya. Lompatan tambahan itulah yang kini dihilangkan Node.js untuk sebagian besar kode.
Mekanismenya disebut type stripping, dan model mentalnya lebih sederhana daripada transpiler mana pun. Node tidak memahami tipe Anda, tidak mengompilasinya, dan tidak menalarnya. Node menghapusnya. Yang tersisa adalah JavaScript biasa, dan itulah yang dijalankan mesin V8. Begitu Anda memahami satu kalimat itu, setiap kemampuan dan setiap keterbatasan fitur ini mengikuti dari sana.
Ketika Node menemui file .ts, ia menyerahkan sumbernya ke transform internal yang menghapus sintaks TypeScript yang bisa dihapus dan menaruh spasi di tempatnya. Baris seperti const port: number = 3000 menjadi const port = 3000. Titik dua dan anotasi tipenya hilang; posisi karakternya dipertahankan. Trik spasi itu penting: karena tidak ada yang bergeser, stack trace tetap menunjuk ke baris dan kolom yang benar di file asli Anda tanpa perlu source map.
Yang krusial, tidak ada type checking yang terjadi. Node menghapus anotasi lalu lanjut. Jika Anda menetapkan string ke variabel bertipe number itu, Node tidak akan protes, karena pada saat menjalankan kode informasi tipenya sudah tidak ada. Type stripping memberi Anda langkah run secara gratis tetapi bukan jaring pengaman. Perintah di bawah menunjukkan riwayat flag dan pemanggilan compiler yang tetap Anda butuhkan di CI.
# Node 22.6 through 22.17: type stripping is behind a flag
node --experimental-strip-types app.ts
# Node 22.18 / 23.6 and later: no flag, just run the file
node app.ts
# Opt back out if some tool needs the old behavior
node --no-experimental-strip-types app.ts
# Node does NOT type check — run the compiler yourself in CI
npx tsc --noEmitIni hadir secara bertahap, dan mengetahui versi mana melakukan apa menghindarkan kebingungan saat skrip yang jalan di laptop Anda gagal di image CI yang lebih lama:
Ini hal terpenting untuk disampaikan ke tim Anda sebelum mereka mengandalkannya. Menjalankan node app.ts tidak sama dengan menjalankan tsc. Node dengan senang hati mengeksekusi kode yang penuh error tipe, karena ia tidak pernah melihat tipenya sejak awal. Type stripping menggantikan langkah transpile, bukan langkah verifikasi. Editor Anda tetap memeriksa tipe saat Anda menulis, tetapi runtime tidak, dan pipeline Anda pun tidak kecuali Anda membuatnya begitu.
Pertahankan type check sungguhan di CI. Type stripping menghilangkan build, bukan compiler. Jalankan npx tsc --noEmit sebagai tahap pipeline tersendiri agar error tipe menggagalkan build sebelum rilis. Tsconfig yang disarankan untuk setup ini memakai noEmit true dengan target esnext dan module nodenext, karena tsc kini hanya memvalidasi sementara Node yang menjalankan.
Type stripping hanya bekerja pada sintaks yang bisa dihapus dan menyisakan JavaScript yang valid. Anotasi tipe, interface, type alias, dan pernyataan import type semuanya bisa dihapus: cabut semuanya dan sisa kode tetap berdiri sendiri. Sebagian fitur TypeScript berbeda. Sebuah enum, namespace yang berisi kode runtime, parameter properties di konstruktor, dan import alias semuanya perlu diganti oleh JavaScript baru, bukan sekadar dihapus. Stripping biasa tidak bisa melakukannya, jadi Node melempar ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX.
// Erasable syntax — runs under plain type stripping
const port: number = 3000
interface User { id: string; name: string }
import type { IncomingMessage } from "node:http"
// Non-erasable syntax — throws ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX
enum Role { Admin, User }
namespace Api { export const version = 1 }
class Service { constructor(private readonly db: DB) {} }
// Run non-erasable code by asking Node to transform it instead:
node --experimental-transform-types app.ts
// Or lock your codebase to strip-safe syntax in tsconfig.json:
// "erasableSyntaxOnly": trueJika basis kode Anda bergantung pada enum atau namespace dengan kode runtime, node file.ts biasa akan melempar error. Jalankan dengan --experimental-transform-types, ubah konstruk itu menjadi bentuk yang bisa dihapus seperti objek biasa dengan as const, atau setel erasableSyntaxOnly true di tsconfig agar compiler menandai sintaks yang tidak bisa dihapus saat menulis, bukan membiarkannya gagal saat runtime.
Ketika Anda benar-benar butuh fitur yang tidak bisa dihapus itu, --experimental-transform-types mengalihkan Node dari menghapus tipe menjadi benar-benar mengubahnya menjadi JavaScript setara. Mode ini menangani enum, namespace dengan kode runtime, dan konstruk sejenis yang ditolak stripping biasa. Karena transform mode menulis ulang kode alih-alih mengosongkannya, posisi karakter bergeser, jadi padukan dengan --enable-source-maps agar stack trace tetap akurat. Mode ini lebih berat daripada stripping, itulah sebabnya jalur strip-only yang ringan menjadi default.
Node tidak membawa parser TypeScript-nya sendiri. Loader internalnya adalah paket bernama amaro, yang membungkus build WebAssembly dari swc, toolchain TypeScript dan JavaScript berbasis Rust. swc melakukan kerja parse-dan-strip yang cepat; amaro adalah lapisan tipis yang dipakai Node untuk memanggilnya dan agar versi TypeScript yang dibundel bisa diperbarui secara independen dari Node itu sendiri. Inti Rust itulah alasan stripping menambah sangat sedikit overhead startup dibanding transpiler berbasis JavaScript.
| Aspek | Type stripping Node | ts-node atau build tsc |
|---|---|---|
| Type checking | Tidak ada — anotasi dihapus, tidak pernah diverifikasi | Type checking penuh lewat compiler |
| Langkah build | Tidak ada — jalankan file .ts langsung | Langkah transpile atau runner yang berjalan lama |
| Overhead startup | Nyaris nol — swc berbasis Rust lewat amaro | Lebih lambat — transpile JS atau tahap build |
| Enum dan namespace | Ditolak kecuali transform mode aktif | Didukung langsung |
| Paling cocok | Skrip, layanan kecil, siklus dev | Aplikasi besar yang memakai fitur TypeScript penuh |
Aturan praktis saya lugas. Untuk skrip, tooling, dan layanan yang ditulis dalam sintaks yang bisa dihapus, saya menjalankannya dengan node langsung dan membiarkan tahap tsc --noEmit terpisah menangkap error tipe di CI. Untuk basis kode yang bersandar pada enum, namespace, atau decorator, saya memilih transform mode atau tetap memakai build sungguhan. Bagaimanapun, era langkah build wajib hanya untuk mengeksekusi file .ts di server sudah berakhir, dan itu hal yang benar-benar melegakan untuk tidak lagi dipikirkan.
Sumber