Inferensi

Cara compiler menyimpulkan tipe tanpa anotasi dan kapan anotasi eksplisit benar-benar membantu.

Daftar isi

TypeScript dapat menyimpulkan (infer) tipe tanpa diberi anotasi apa pun, hanya dari cara nilai dideklarasikan dan digunakan. Mekanisme ini disebut inferensi tipe (type inference) dan menjadi alasan banyak berkas TypeScript terlihat seperti JavaScript biasa. Topik ini membahas cara compiler menyimpulkan tipe pada variabel dan fungsi, bukti bahwa penyimpulannya nyata, serta kondisi ketika anotasi eksplisit lebih membantu.

Menyimpulkan tipe dari nilai awal#

Ketika variabel dideklarasikan dengan nilai awal, compiler mengaitkan tipenya dengan nilai tersebut tanpa perlu anotasi. Penugasan const namaBuku = "Atlas Dunia" menghasilkan variabel bertipe string, sama persis seolah-olah : string ditulis secara eksplisit. Penyimpulan yang sama berlaku untuk angka, nilai logis, dan hasil pemanggilan fungsi.

Berkas berikut tidak memuat satu anotasi pun, tetapi seluruh variabelnya tetap bertipe dan diperiksa.

ts tanpa-anotasi.ts
const namaBuku = "Atlas Dunia";
const hargaSewa = 2500;
const tersedia = true;

console.log(namaBuku);
console.log(hargaSewa);
console.log(tersedia);
text Keluaran
Atlas Dunia
2500
true

Ketiga variabel pada berkas tersebut tetap diperiksa tipe sepenuhnya oleh compiler. Penugasan angka ke namaBuku akan ditolak dengan pesan error yang sama seperti pada variabel yang dianotasi eksplisit. Dari sisi keamanan tipe keduanya setara, sedangkan perbedaannya hanya pada kejelasan maksud yang tertulis di kode.

Inferensi juga bekerja pada hasil ekspresi dan pemanggilan fungsi. Deklarasi const total = hargaSewa * 2 disimpulkan bertipe number karena kedua operannya bertipe number. Rantai penyimpulan ini berlaku berjenjang, sehingga tipe dari satu deklarasi ikut mengalir ke deklarasi berikutnya yang memakainya. Dengan demikian, sebagian besar berkas kecil dapat ditulis tanpa satu anotasi pun tanpa kehilangan pemeriksaan.

Membuktikan hasil inferensi lewat pesan error#

Penyimpulan juga berlaku pada tipe kembalian fungsi. Fungsi yang mengembalikan ekspresi aritmetika disimpulkan bertipe kembalian number tanpa anotasi apa pun. Bukti bahwa compiler benar-benar menyimpulkan tipe ini dapat dilihat ketika hasilnya dipakai secara keliru.

ts inferensi-salah.ts
// tidak-dijalankan
function bungaSederhana(pokok: number, persen: number) {
  return (pokok * persen) / 100;
}

const bunga = bungaSederhana(200000, 5);
bunga.toUpperCase();
bash
# menampilkan pesan error tanpa mengeksekusi berkas
tsc --noEmit inferensi-salah.ts
text Keluaran
inferensi-salah.ts(7,7): error TS2339: Property 'toUpperCase' does not exist on type 'number'.

Pesan error menyebut tipe number, padahal tidak ada satu anotasi kembalian pun pada fungsi bungaSederhana. Tipe tersebut murni hasil penyimpulan compiler atas ekspresi (pokok * persen) / 100. Dengan begitu, kesalahan pemakaian metode teks pada hasil perhitungan tertangkap sebelum program dijalankan, tanpa perlu menulis anotasi tambahan. Pola pemeriksaan yang sama berlaku untuk metode milik tipe tunggal lain, misalnya toFixed yang hanya dimiliki number.

Peluasan tipe pada let dan const#

Hasil inferensi pada let dan const tidak selalu sama. Variabel let yang diinisialisasi angka disimpulkan bertipe number, sehingga dapat ditugaskan angka lain apa pun. Pada const, compiler menetapkan tipe yang lebih presisi, yaitu tipe literal dari nilai itu sendiri, misalnya 12 untuk const jumlahBaris = 12. Perilaku peluasan pada let ini disebut widening.

ts widening-let.ts
let jumlahBaris = 12;
jumlahBaris = 30;

console.log(jumlahBaris);
text Keluaran
30

Variabel jumlahBaris bertipe number, bukan tipe literal 12, sehingga penugasan ulang dengan 30 diterima. Bila deklarasi diganti menjadi const, penugasan ulang itu justru ditolak karena konstanta tidak dapat diubah. Perbedaan ini menunjukkan bahwa inferensi memperhatikan kata kunci deklarasi, bukan hanya nilainya.

Kapan anotasi eksplisit benar-benar membantu#

Inferensi tidak menggantikan anotasi, karena ada tiga situasi yang justru membutuhkannya. Situasi pertama adalah parameter fungsi, yang tipenya tidak mungkin disimpulkan karena nilai baru muncul saat pemanggilan. Situasi kedua adalah deklarasi yang dipisah dari inisialisasinya, sehingga maksud tipe perlu ditulis sejak awal. Situasi ketiga adalah tanda tangan fungsi yang dipakai lintas berkas, tempat anotasi berfungsi sebagai kontrak yang terbaca.

ts anotasi-berguna.ts
let skorAkhir: number;
let predikat: string;

skorAkhir = 87;
predikat = "baik";

function konversiMenitKeJam(menit: number): string {
  return `${Math.floor(menit / 60)} jam ${menit % 60} menit`;
}

console.log(skorAkhir, predikat);
console.log(konversiMenitKeJam(135));
text Keluaran
87 baik
2 jam 15 menit

Variabel skorAkhir dan predikat dideklarasikan tanpa nilai, sehingga anotasinya menentukan tipe yang sah untuk penugasan berikutnya. Fungsi konversiMenitKeJam menganotasi parameter dan kembaliannya secara eksplisit, walaupun compiler dapat menyimpulkan kembaliannya. Anotasi tersebut menjadikan fungsi siap dipanggil dari berkas lain tanpa membaca isinya.

Anotasi pada tanda tangan fungsi juga melindungi dari perubahan yang tidak tampak. Bila isi fungsi dirombak dan kembaliannya berubah dari teks menjadi angka, compiler langsung melaporkan semua pemanggil yang tidak sesuai. Tanpa anotasi, perubahan tersebut diam-diam menyebar ke berkas lain dan baru terlihat sebagai error di tempat yang jauh dari sumbernya. Biaya menulis satu anotasi jauh lebih kecil daripada biaya menelusuri kesalahan semacam ini.

Kesalahan umum#

Kesalahan berikut terjadi ketika anotasi dan inferensi dipakai tanpa mempertimbangkan tempatnya masing-masing.

  • Menganotasi setiap variabel lokal meskipun nilainya langsung terlihat. Anotasi berlebihan memperpanjang kode tanpa menambah pemeriksaan apa pun. Biarkan inferensi bekerja, lalu tambahkan anotasi hanya pada tiga situasi di atas.
  • Mengira inferensi berarti tipe longgar. Hasil penyimpulan tetap diperiksa ketat, sehingga penugasan nilai bertipe lain tetap ditolak compiler.
  • Mengubah isi fungsi publik tanpa memperhatikan tipe kembaliannya. Bila anotasi kembalian tidak ditulis, perubahan tipe kembalian baru terlihat pada berkas pemanggil dengan pesan yang jauh dari sumbernya.

Ringkasan#

  • Compiler menyimpulkan tipe dari nilai awal dan ekspresi kembalian fungsi, dan hasil penyimpulannya diperiksa sama ketatnya dengan anotasi eksplisit.
  • Variabel let meluaskan hasil inferensi menjadi number atau string, sedangkan const mempertahankan tipe literalnya.
  • Anotasi eksplisit dibutuhkan pada parameter fungsi, deklarasi tanpa inisialisasi, dan tanda tangan fungsi yang dipakai lintas berkas.
  • Pesan error adalah cara cepat membuktikan hasil inferensi, karena tipe yang disimpulkan ikut disebut di dalamnya.

Referensi#