TypeScript menyediakan dua cara untuk memberi nama pada bentuk objek, yaitu type alias melalui kata kunci type dan interface melalui kata kunci interface. Keduanya menghasilkan tipe bernama yang diperiksa compiler dengan aturan structural typing, yaitu pencocokan berdasarkan bentuk objek, bukan berdasarkan nama tipenya. Topik ini membandingkan keduanya dari sisi sintaks, perluasan tipe, perbedaan pesan error, dan kondisi ketika salah satunya lebih unggul. Setelah membacanya, pemilihan antara type dan interface dapat diputuskan berdasarkan kebutuhan konkret, bukan sekadar kebiasaan.
Dua sintaks untuk bentuk yang sama#
Sebuah type alias dideklarasikan dengan kata kunci type, diikuti nama tipe, tanda sama dengan, lalu bentuk tipe yang dimaksud. Sebuah interface dideklarasikan dengan kata kunci interface, diikuti nama dan badan kurung kurawal tanpa tanda sama dengan. Keduanya mendeskripsikan daftar properti beserta tipenya, dan sebuah objek yang memenuhi salah satunya otomatis memenuhi yang lain.
type CatatanAlias = {
judul: string;
halaman: number;
};
interface CatatanInterface {
judul: string;
halaman: number;
}
const catatanA: CatatanAlias = { judul: "Log Suhu", halaman: 12 };
const catatanB: CatatanInterface = { judul: "Log Suhu", halaman: 12 };
console.log(catatanA.judul, catatanA.halaman);
console.log(catatanB.judul, catatanB.halaman);Log Suhu 12
Log Suhu 12Kedua deklarasi pada contoh menghasilkan tipe yang dapat saling menggantikan. Nilai bertipe CatatanAlias dapat diberikan ke variabel bertipe CatatanInterface tanpa konversi apa pun karena bentuknya identik. Perbedaan di antara keduanya baru terasa ketika tipe perlu dikembangkan, digabungkan, atau ketika terjadi kesalahan tipe.
Nama tipe pada keduanya mengikuti konvensi PascalCase, yaitu penulisan nama dengan huruf kapital di awal setiap kata. Compiler tidak memaksanya, tetapi konvensi itu membuat tipe mudah dibedakan dari variabel. Type alias juga menjadi satu-satunya jalan untuk menamai tipe non-objek, misalnya type Skor = number atau type Kategori = "buku" | "majalah", karena interface tidak menerima bentuk semacam itu. Untuk bentuk objek murni, pilihan di antara keduanya bebas dilakukan terlebih dahulu, lalu dinilai ulang dari sisi ekstensi pada bagian berikutnya.
Ekstensi dengan extends dan tanda &#
Interface memperluas tipe lain dengan kata kunci extends, sedangkan type alias membentuk irisan tipe dengan tanda & yang disebut intersection. Hasil kedua pendekatan ini mirip, yaitu tipe baru yang memuat seluruh properti tipe asalnya ditambah properti tambahan. Interface boleh memperluas lebih dari satu interface sekaligus, misalnya interface C extends A, B, sedangkan alias menggabungkan beberapa tipe dengan rantai A & B & C.
interface Dokumen {
judul: string;
}
interface DokumenTerlindungi extends Dokumen {
kataSandi: string;
}
type DokumenSajaBaca = Dokumen & { terkunci: boolean };
const a: DokumenTerlindungi = { judul: "Laporan", kataSandi: "rahasia" };
const b: DokumenSajaBaca = { judul: "Risalah", terkunci: true };
console.log(a.judul, a.kataSandi);
console.log(b.judul, b.terkunci);Laporan rahasia
Risalah trueInterface memiliki kemampuan tambahan yang tidak dimiliki type alias, yaitu declaration merging yang menggabungkan dua deklarasi dengan nama yang sama. Apabila dua interface bernama sama berada pada cakupan yang sama, keduanya digabungkan menjadi satu tipe yang memuat semua propertinya. Deklarasi type alias ganda justru ditolak karena nama yang sama tidak boleh dipakai dua kali.
interface DataNilai {
mataPelajaran: string;
}
interface DataNilai {
skor: number;
}
const nilai: DataNilai = { mataPelajaran: "Matematika", skor: 92 };
console.log(`${nilai.mataPelajaran}: ${nilai.skor}`);Matematika: 92Berkas berikut menunjukkan sisi lain dari perbandingan tersebut, yaitu type alias yang dideklarasikan dua kali.
// tidak-dijalankan
type DataNilai = { mataPelajaran: string };
type DataNilai = { skor: number };
console.log("tidak pernah berjalan");# berkas ini sengaja tidak lolos pemeriksaan tipe
tsc --noEmit type-ganda.tstype-ganda.ts(2,6): error TS2300: Duplicate identifier 'DataNilai'.
type-ganda.ts(3,6): error TS2300: Duplicate identifier 'DataNilai'.Perbedaan pada pesan error#
Perbedaan yang paling terasa muncul ketika tipe turunan bertentangan dengan tipe asalnya. Interface yang memperluas tipe lain tetapi menimpa properti dengan tipe yang tidak cocok langsung ditolak compiler pada baris deklarasinya, lengkap dengan nama kedua interface yang bersangkutan.
// tidak-dijalankan
interface Dokumen {
judul: string;
}
interface DokumenTerlindungi extends Dokumen {
judul: number;
}
console.log("tidak pernah berjalan");# berkas ini sengaja tidak lolos pemeriksaan tipe
tsc --noEmit ekstensi-bentrok.tsekstensi-bentrok.ts(6,11): error TS2430: Interface 'DokumenTerlindungi' incorrectly extends interface 'Dokumen'.
Types of property 'judul' are incompatible.
Type 'number' is not assignable to type 'string'.Type alias yang digabungkan dengan & tidak mendapat pemeriksaan serupa saat dideklarasikan. Deklarasi yang bertentangan tetap diterima, lalu error baru muncul ketika tipe hasilnya digunakan, dengan pesan yang jauh lebih teknis.
// tidak-dijalankan
type Koleksi = { nama: string; jenis: "buku" | "majalah" };
type JenisArsip = Koleksi & { jenis: "arsip" };
function tampilkan(item: JenisArsip): void {
console.log(item.nama);
}# deklarasi intersection lulus, error baru muncul saat tipe digunakan
tsc --noEmit intersection-senyap.tsintersection-senyap.ts(6,20): error TS2339: Property 'nama' does not exist on type 'never'.
The intersection 'JenisArsip' was reduced to 'never' because property 'jenis' has conflicting types in some constituents.Kapan masing-masing unggul#
Tabel berikut merangkum kondisi yang membuat salah satu pilihan lebih tepat.
| Kebutuhan | Pilihan yang tepat | Alasan |
|---|---|---|
| Union, tuple, atau tipe primitif bernama | type | Interface hanya mendeskripsikan bentuk objek. |
| Perluasan bertingkat bentuk objek | interface | extends mudah dibaca dan konfliknya ditangkap sejak deklarasi. |
| Penggabungan deklarasi lintas berkas | interface | Dua interface bernama sama otomatis digabungkan. |
Komposisi dengan & dan | | type | Komposisi tipe hanya dapat ditulis lewat alias. |
Panduan resmi TypeScript menyatakan bahwa perbedaan keduanya halus, sehingga keputusan yang disarankan adalah konsistensi satu gaya di seluruh proyek. Halaman yang sama memberi saran sederhana, yaitu memakai interface sampai fitur khusus type alias benar-benar dibutuhkan. Panduan itu juga mencatat bahwa interface dengan extends sering lebih ringan diproses compiler daripada alias dengan intersection, karena deklarasi interface disimpan dalam cache menurut namanya. Untuk union, tuple, primitif, dan komposisi lainnya, type alias tetap menjadi satu-satunya pilihan yang tersedia.
Kesalahan umum#
Mengharapkan type alias bergabung secara otomatis adalah kekeliruan yang berasal dari kebiasaan pada interface. Deklarasi type ganda dengan nama sama selalu menghasilkan error TS2300, sehingga properti baru harus dimasukkan ke alias yang sudah ada atau digabungkan lewat &. Perbaikannya adalah memilih satu gaya sejak awal proyek dan menaatinya secara konsisten.
Kekeliruan kedua adalah menganggap intersection selalu sah selama sintaksnya benar. Contoh Koleksi & { jenis: "arsip" } diterima compiler, tetapi hasilnya adalah tipe never yang tidak dapat diisi, dan error baru muncul di tempat pemakaian yang bisa berjauhan dari deklarasinya. Perbaikannya adalah memperlebar tipe literal menjadi satu union yang memuat semua nilai yang sah, misalnya "buku" | "majalah" | "arsip", sehingga intersection yang terbentuk tidak pernah kosong.
Kekeliruan ketiga adalah menggabungkan dua interface yang memiliki properti bernama sama tetapi bertipe berbeda. Penggabungan deklarasi memang menerima dua interface bernama sama, tetapi properti yang sama wajib bertipe identik, sehingga compiler menolaknya dengan error TS2717. Perbaikannya adalah memberi nama properti yang berbeda atau memindahkan perubahan tipe ke interface turunan, bukan menimpanya lewat penggabungan deklarasi.
Ringkasan#
typedaninterfacemenghasilkan tipe objek yang setara secara struktural, dengan perbedaan sintaks berupa tanda sama dengan dan kata kunci yang digunakan.- Interface mendukung
extends, declaration merging, dan penolakan konflik sejak deklarasi, sedangkan type alias mendukung union, tuple, primitif, dan intersection. - Konflik pada interface ditangkap beserta nama tipenya, sedangkan konflik pada intersection baru muncul saat tipe digunakan dalam bentuk
never. - Handbook resmi menyarankan konsistensi satu gaya untuk seluruh proyek karena perbedaan keduanya memang halus.
Referensi#
- Everyday Types — The TypeScript Handbook — Memuat bagian Differences Between Type Aliases and Interfaces yang menjadi dasar perbandingan ini.
- Object Types — The TypeScript Handbook — Mendeskripsikan cara interface dan type alias membentuk tipe objek beserta propertinya.