Implements

Membuat kelas yang memenuhi interface dan melihat structural typing dalam praktik.

Daftar isi

Topik ini membahas klausul implements, yaitu cara mengaitkan kelas dengan sebuah interface yang berperan sebagai kontrak bentuk. Pembahasan mencakup penulisan kelas yang memenuhi kontrak, pemeriksaan yang terjadi ketika kepatuhan gagal, serta structural typing yang menjadi dasar seluruh pemeriksaan tersebut. Setelah topik ini dipelajari, kelas dapat dimodelkan mengikuti kontrak tipe dengan tepat dan pesan error pemeriksa tipe dapat dibaca dengan cepat.

Interface sebagai kontrak untuk kelas#

Interface adalah tipe yang mendefinisikan bentuk objek melalui daftar properti dan metode (method), yaitu fungsi yang terikat pada kelas. Daftar itu ditulis beserta tipe setiap anggotanya dan tidak memuat implementasi sama sekali. Sebuah kelas menyatakan kepatuhannya melalui klausul implements yang ditulis setelah nama kelas. Dengan deklarasi ini, pemeriksa tipe menuntut setiap anggota pada interface tersedia pada kelas dengan tipe yang cocok. Hubungan interface dan kelas tampak pada fragmen berikut.

ts Bentuk umum implements
interface TugasPiket {
  nama: string;
  uraian(): string;
}

class PiketRuangan implements TugasPiket {
  nama: string = "Citra";

  uraian(): string {
    return `${this.nama} merapikan ruang kelas`;
  }
}

Satu kelas boleh mengimplementasikan lebih dari satu interface sekaligus, misalnya implements TugasPiket, Terarsip, dengan memisahkan namanya menggunakan koma. Sebaliknya, satu interface boleh diimplementasikan oleh banyak kelas karena kontrak tidak mengenal pemilik tunggal. Hubungan ini bersifat deklarasi dan bukan pewarisan, sehingga kelas tidak menerima satu baris implementasi pun dari interface.

Seluruh pemeriksaan tersebut hanya berlangsung saat kompilasi. Hasil kompilasi tidak memuat interface maupun klausul implements karena keduanya hilang bersama type erasure, yaitu proses penghapusan seluruh anotasi tipe dari program. Konsekuensinya, kepatuhan terhadap kontrak tidak diperiksa ulang ketika program berjalan.

Kelas yang memenuhi interface#

Berkas piket.ts berikut memuat kelas PiketHarian yang mengimplementasikan interface TugasPiket. Field, yaitu variabel yang melekat pada objek, dideklarasikan dengan tipe string dan diisi melalui konstruktor. Konstruktor sendiri adalah metode khusus yang dijalankan ketika objek dibuat dengan kata kunci new. Metode uraian() memenuhi anggota kontrak yang sama, sedangkan field selesai beserta metode tandaiSelesai() merupakan tambahan di luar kontrak.

ts piket.ts
interface TugasPiket {
  nama: string;
  hari: string;
  uraian(): string;
}

class PiketHarian implements TugasPiket {
  nama: string;
  hari: string;
  selesai: boolean = false;

  constructor(nama: string, hari: string) {
    this.nama = nama;
    this.hari = hari;
  }

  uraian(): string {
    return `${this.nama} bertugas pada hari ${this.hari}`;
  }

  tandaiSelesai(): void {
    this.selesai = true;
  }
}

const jadwal = new PiketHarian("Ali", "Sabtu");
console.log(jadwal.uraian());
jadwal.tandaiSelesai();
console.log(jadwal.selesai);
text Keluaran
Ali bertugas pada hari Sabtu
true

Keluaran menunjukkan kedua panggilan berjalan seperti kelas biasa, karena implements tidak mengubah perilaku program sama sekali. Anggota tambahan diperbolehkan karena interface hanya menetapkan batas minimum, sehingga kelas tetap dianggap memenuhi kontrak meskipun anggotanya lebih banyak.

Pemeriksaan menjangkau setiap anggota kontrak, bukan hanya keberadaannya. Metode pada kontrak diperiksa sampai ke tipe parameter dan tipe kembaliannya, sehingga uraian() yang menerima parameter tambahan atau mengembalikan tipe lain ditolak. Field pada kontrak juga diperiksa ketepatan tipenya, misalnya field bertipe string tidak boleh diisi number. Dengan cakupan selengkap itu, kontrak dapat dijadikan patokan bentuk data yang dilalui banyak kelas.

Apabila tipe salah satu anggota kontrak tidak cocok, misalnya uraian() mengembalikan number, pemeriksa tipe menolak kelasnya dengan pesan yang menunjuk langsung pada anggota yang bermasalah. Pesan semacam ini membuat perbedaan kontrak dan implementasi terlihat jelas pada laporan pemeriksa tipe. Perbaikan cukup dilakukan pada satu sisi saja, yaitu kelas atau kontrak, tanpa menyentuh kode pemakai lain.

Pemeriksaan saat kepatuhan gagal#

Kelas yang tidak melengkapi seluruh anggota kontrak ditolak pada saat kompilasi. Berkas piket-salah.ts berikut sengaja memuat kelas tanpa metode uraian(), sehingga berkas ini tidak dijalankan dan hanya diperiksa tipenya. Perintah sesudahnya meminta pemeriksa tipe memeriksa berkas tersebut tanpa menghasilkan keluaran program.

ts piket-salah.ts
// tidak-dijalankan
interface TugasPiket {
  nama: string;
  hari: string;
  uraian(): string;
}

class PiketDarurat implements TugasPiket {
  nama = "Rina";
  hari = "Ahad";
}
bash
# periksa tipe tanpa menghasilkan berkas keluaran
tsc --noEmit --pretty false piket-salah.ts
text Keluaran
piket-salah.ts(8,7): error TS2420: Class 'PiketDarurat' incorrectly implements interface 'TugasPiket'.
  Property 'uraian' is missing in type 'PiketDarurat' but required in type 'TugasPiket'.

Error TS2420 menyatakan kelas tidak mengimplementasikan interface dengan benar, sedangkan baris kedua pesan itu menunjuk anggota yang kurang beserta tipenya. Perbaikannya ada dua arah, yaitu melengkapi anggota yang hilang pada kelas atau menyesuaikan kontrak bila anggota tersebut memang tidak diperlukan. Pemeriksaan ini terjadi sebelum program berjalan, sehingga kegagalan kepatuhan tidak pernah sampai ke pengguna.

Perhatikan pula bahwa penolakan mencakup dua kasus, yaitu anggota yang kurang maupun anggota yang tipenya tidak cocok. Pesannya selalu menunjuk kelas yang bermasalah terlebih dahulu, baru rincian anggotanya, sehingga pola pembacaannya konsisten antarkasus. Kebiasaan membaca pesan semacam ini mempercepat penyelidikan ketika kontrak yang dipakai terdiri atas banyak anggota.

Structural typing dalam praktik#

TypeScript menilai kecocokan tipe berdasarkan bentuk nilainya, bukan berdasarkan nama tipe ataupun adanya klausul implements. Pendekatan ini disebut structural typing, yaitu pemeriksaan yang membandingkan anggota satu tipe dengan tipe lain secara berlapis. Konsekuensinya, objek tanpa deklarasi implements pun dapat dipakai di posisi yang menuntut interface, selama bentuknya lengkap dan tipenya cocok.

Berkas struktural.ts berikut memanggil fungsi tampilkan() dengan dua nilai berbeda. Nilai pertama adalah instance, yakni objek yang dibuat dari kelas dengan kata kunci new. Nilai kedua berupa objek literal, yaitu objek yang ditulis langsung di dalam kurung kurawal.

ts struktural.ts
interface TugasPiket {
  nama: string;
  hari: string;
  uraian(): string;
}

function tampilkan(tugas: TugasPiket): void {
  console.log(tugas.uraian());
}

class PiketPerpustakaan implements TugasPiket {
  nama: string;
  hari: string;

  constructor(nama: string, hari: string) {
    this.nama = nama;
    this.hari = hari;
  }

  uraian(): string {
    return `${this.nama} mengurus perpustakaan pada hari ${this.hari}`;
  }
}

tampilkan(new PiketPerpustakaan("Budi", "Senin"));
tampilkan({
  nama: "Dewi",
  hari: "Rabu",
  uraian: () => "Dewi menata ruang baca pada hari Rabu",
});
text Keluaran
Budi mengurus perpustakaan pada hari Senin
Dewi menata ruang baca pada hari Rabu

Kedua nilai diterima karena sama-sama memiliki nama bertipe string, hari bertipe string, dan uraian() yang mengembalikan string. Klausul implements pada kelas berfungsi sebagai pernyataan niat sekaligus penanda bagi pembaca kode, sedangkan keputusan akhir tetap berasal dari perbandingan bentuk. Satu pengecualian perlu diingat, yaitu objek literal yang ditulis langsung pada pemanggilan diperiksa lebih ketat. Properti ekstra pada objek literal semacam itu ditolak dengan error TS2353, sedangkan properti ekstra pada kelas diperbolehkan.

Kesalahan umum#

Tiga kesalahan berikut sering muncul saat klausul implements baru dipakai. Ketiganya bukan kegagalan sintaks, melainkan salah paham tentang letak pemeriksaan dan batas kontraknya. Pesan error yang menyertainya justru mempercepat perbaikan.

  • Mengira implements memengaruhi program yang sudah berjalan. Klausul ini hilang bersama type erasure, sehingga tidak ada pemeriksaan kontrak pada saat runtime. Perbaikannya adalah mengandalkan laporan pemeriksa tipe pada saat kompilasi.
  • Menulis properti ekstra pada objek literal yang ditempatkan langsung di posisi bertipe interface. Pemeriksa tipe menolaknya dengan error TS2353 meskipun bagian intinya cocok. Perbaikannya adalah menyimpan objek pada variabel terlebih dahulu atau menambahkan properti tersebut ke interface.
  • Menyamakan implements dengan extends. Klausul implements tidak membawa implementasi apa pun karena interface tidak memuat isi, sedangkan pewarisan dengan extends menyalin anggota yang sudah jadi. Pilih implements untuk kontrak dan extends untuk perilaku bawaan.

Ringkasan#

  • Klausul implements menyatakan kepatuhan kelas terhadap interface dan diperiksa sepenuhnya pada saat kompilasi.
  • Kelas wajib menyediakan seluruh anggota kontrak dengan tipe yang cocok, sedangkan anggota tambahan diperbolehkan.
  • Kepatuhan yang gagal menghasilkan error TS2420 sebelum program berjalan, lengkap dengan nama anggota yang kurang.
  • Structural typing menilai kecocokan dari bentuk nilai, sehingga objek tanpa implements pun diterima bila bentuknya lengkap.

Referensi#