Cara Kerja TypeScript

Compiler tsc, type erasure, dan mental model TypeScript tanpa biaya saat runtime.

Daftar isi

Topik ini menjelaskan mekanisme yang bekerja di balik TypeScript, yaitu tugas compiler tsc, hilangnya tipe pada hasil kompilasi (type erasure), dan mental model yang menyertainya. Ketiga mekanisme itu dijelaskan melalui alur perintah nyata beserta keluarannya, sehingga setiap langkah dapat diulang di terminal. Pemisahan antara waktu pemeriksaan dan waktu eksekusi menjadi kunci yang perlu diingat dari topik ini.

Dua tugas compiler tsc#

Compiler TypeScript disebut tsc dan menjalankan dua tugas yang berbeda. Tugas pertama adalah pemeriksaan tipe (type checking), yaitu mencocokkan setiap nilai dengan anotasinya dan melaporkan error berupa pesan yang presisi. Tugas kedua adalah penerjemahan, yaitu menghasilkan berkas JavaScript dari kode sumber yang sama. Kedua tugas tersebut berkaitan tetapi terpisah, karena laporan error tidak mengubah isi penerjemahan dan penerjemahan tidak menuntut kode bebas error.

Alur lengkapnya digambarkan pada diagram berikut. Berkas sumber masuk ke tsc, lalu hasilnya berupa dua kemungkinan keluaran, yaitu pesan error untuk pelanggaran tipe dan berkas JavaScript untuk dijalankan atau disebarkan.

berkas .tskode + anotasi tipetsccompilerpesan errorbila ada pelanggaranberkas .jstanpa anotasi tipedibacamelaporkanmenerjemahkan
Dua keluaran tsc: pesan error dan berkas JavaScript

Perintah tsc memiliki dua bentuk yang memisahkan kedua tugas itu. Bentuk tsc --noEmit berkas.ts hanya memeriksa tipe tanpa menulis berkas apa pun, sehingga cocok untuk pemeriksaan cepat. Bentuk tsc berkas.ts memeriksa sekaligus menerjemahkan, dan berkas .js yang dihasilkan berada di sebelah sumbernya.

Kata noEmit pada opsi pertama berasal dari istilah emit, yaitu pencetakan hasil penerjemahan ke berkas keluaran. Dengan kata lain, opsi tersebut memerintahkan tsc untuk memeriksa lalu berhenti, dan bentuk inilah yang paling sering dipanggil saat mengembangkan. Kedua bentuk perintah juga menerima lebih dari satu berkas sekaligus, sehingga pemeriksaan beberapa berkas tetap memungkinkan tanpa konfigurasi proyek.

Pemeriksaan tipe sebelum program berjalan#

Berkas berikut sengaja mengandung ketidakcocokan tipe. Variabel sisi dianotasi sebagai teks, sedangkan fungsi luasPersegi menuntut bilangan.

ts tipe-salah.ts
// tidak-dijalankan
function luasPersegi(sisi: number): number {
  return sisi * sisi;
}

const sisi: string = "lima";
console.log(luasPersegi(sisi));

Pemeriksaan dijalankan dengan opsi –noEmit agar tidak ada berkas keluaran yang ikut dibuat.

bash
# memeriksa tipe tanpa menghasilkan berkas keluaran
tsc --noEmit tipe-salah.ts
text Keluaran
tipe-salah.ts(7,25): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.

Pesan error memuat lokasi yang presisi, yaitu berkas tipe-salah.ts baris 7 kolom 25, diikuti kode error TS2345 dan penjelasan ketidakcocokannya. Program sama sekali tidak perlu dijalankan untuk menemukan masalah ini, karena pemeriksaan terjadi pada fase kompilasi. Bandingkan dengan cacat NaN pada pembahasan superset, yang baru terlihat setelah program berjalan dan kadang hanya pada jalur eksekusi tertentu.

Setiap pesan error tsc mengikuti pola yang sama, yaitu nama berkas, posisi baris dan kolom, kode error, lalu penjelasan ringkas. Kode TS2345 menandai argumen yang tidak cocok dengan parameternya, dan penamaan kode semacam ini konsisten, sehingga pesan serupa mudah dicari pada dokumentasi. Kode keluar (exit code) perintah tsc juga berguna, yaitu bukan nol saat ada error, sehingga pemeriksaan ini dapat dirangkai ke dalam skrip dan otomatisasi tanpa alat tambahan.

Type erasure#

Type erasure adalah proses penghapusan seluruh konstruksi tipe dari kode saat kompilasi berlangsung. Anotasi, alias tipe, dan parameter tipe tidak meninggalkan jejak apa pun pada berkas hasil penerjemahan. Konsekuensinya, runtime yang menjalankan hasil kompilasi tidak mengetahui bahwa kode itu pernah ditulis dalam TypeScript.

Berkas sapaan.ts menjadi contoh yang ditelusuri dari pemeriksaan hingga keluarannya. Program ini dapat dijalankan secara langsung oleh runtime yang mengenali TypeScript maupun setelah dikompilasi, dan keduanya menghasilkan keluaran yang sama.

ts sapaan.ts
function sapa(nama: string): string {
  return `Halo, ${nama}!`;
}

console.log(sapa("Ali"));
text Keluaran
Halo, Ali!

Jalur klasiknya berupa kompilasi terlebih dahulu, kemudian eksekusi berkas hasilnya dengan Node.js.

bash
# mengompilasi sapaan.ts menjadi JavaScript
tsc sapaan.ts
# menjalankan hasil kompilasi dengan Node.js
node sapaan.js
text Keluaran
Halo, Ali!
bash
# menampilkan isi berkas hasil kompilasi
cat sapaan.js
text Keluaran
"use strict";
function sapa(nama) {
    return `Halo, ${nama}!`;
}
console.log(sapa("Ali"));

Isi berkas hasil kompilasi memuat JavaScript biasa tanpa satu anotasi tipe pun. Kata function, isi fungsi, dan pemanggilan console.log persis seperti yang ditulis tangan dalam JavaScript. Baris "use strict" ditambahkan tsc sebagai penanda mode ketat JavaScript, sedangkan sisanya murni penerjemahan. Inilah bukti type erasure, yaitu tipe berakhir sebagai pengetahuan waktu desain dan tidak pernah sampai ke runtime. Berkas sapaan.js kemudian dihapus agar direktori kerja tetap bersih.

bash
# menghapus berkas hasil kompilasi setelah diperiksa
rm sapaan.js

Mental model tanpa biaya runtime#

Mental model yang tepat memisahkan dua dunia dengan tegas. Pada waktu penulisan dan kompilasi, tipe hadir penuh dan diperiksa compiler. Pada waktu program berjalan, hanya JavaScript yang ada, dan berkas hasil kompilasi berjalan dengan kecepatan setara JavaScript biasa karena bentuknya memang JavaScript biasa. Satu-satunya biaya tambahan TypeScript adalah waktu kompilasi, bukan kecepatan program.

Pemisahan ini menuntut kehati-hatian pada satu titik, yaitu data yang berasal dari luar program. Anotasi tipe tidak pernah memeriksa isi respons API, isi berkas, atau masukan pengguna, karena pemeriksaannya sudah selesai sebelum data masuk. Konversi dan validasi pada batas sistem tetap menjadi pekerjaan pengembang, kemudian anotasi menjaga kepatuhan setelah data dinyatakan sah.

Konstruksi tipe modern hampir semuanya hilang sepenuhnya, misalnya alias type, antarmuka, dan parameter tipe generic. Pengecualian kecilnya berupa konstruksi warisan seperti kata kunci enum yang tetap menghasilkan objek saat runtime, dan pendekatan terkini umumnya menghindarinya. Dengan mental model yang benar, pertanyaan apa pun tentang perilaku TypeScript cukup dijawab dengan satu langkah, yaitu melihat berkas JavaScript hasil kompilasinya.

Kesalahan umum#

Kesalahan berikut sering muncul setelah pemahaman tentang compiler mulai terbentuk, dan semuanya berkaitan dengan penempatan tanggung jawab.

  • Mengira program dengan error tipe pasti ditolak saat dijalankan. Node dan browser menjalankan JavaScript tanpa memeriksa tipe, sehingga berkas hasil kompilasi yang sudah ada tetap berjalan meskipun kode sumber terbarunya baru saja ditolak compiler. Perbaikannya adalah menjadikan tsc --noEmit gerbang wajib sebelum berkas hasil kompilasi digunakan atau disebarkan.
  • Memeriksa satu berkas lalu mengira seluruh proyek sudah diperiksa. Pemanggilan tsc berkas.ts hanya mencakup berkas yang disebut beserta dependensinya, sedangkan berkas lain yang tidak tersentuh tetap lolos begitu saja. Pemeriksaan menyeluruh memerlukan tsconfig.json yang membawahi semua berkas proyek sekaligus.
  • Mencari jejak tipe pada runtime ketika perilaku program terasa aneh. Tipe sudah terhapus saat program berjalan, sehingga penyelidikan yang tepat dilakukan pada kode sumber dan pesan compiler. Perbaikannya adalah membaca berkas hasil kompilasi untuk memastikan apa yang benar-benar dieksekusi, lalu menelusuri pesan compiler pada sumbernya.

Ringkasan#

  • tsc menjalankan dua tugas terpisah, yaitu memeriksa kepatuhan tipe dan menerjemahkan kode menjadi JavaScript.
  • Error tipe dilaporkan pada fase kompilasi lengkap dengan nama berkas, baris, kolom, dan kode error seperti TS2345.
  • Type erasure menghapus seluruh anotasi tipe sehingga berkas hasil kompilasi adalah JavaScript murni.
  • Biaya TypeScript berada pada waktu penulisan dan kompilasi, sedangkan runtime menjalankan program tanpa pengetahuan tentang tipe.

Referensi#