Kalau kamu pernah menulis class di JavaScript biasa, sebenarnya kamu sudah pegang sebagian besar bekalnya. Class itu seperti cetakan: kamu menggambarkan bentuk sebuah objek dan apa saja yang bisa dilakukannya, lalu kamu mencetak objek dari situ sebanyak yang kamu mau. TypeScript tidak membuang konsep itu. Sintaksnya tetap sama, hanya ditambah satu hal di atasnya — tipe — supaya editor bisa menangkap kesalahan sebelum kodemu sempat dijalankan.
Artikel ini membahas apa saja yang ditambahkan TypeScript ke sebuah class, satu per satu. Kita mulai dari class JavaScript yang sudah familier, lalu pelan-pelan menambahkan field bertipe, access modifier, readonly, parameter property, interface, dan sekilas abstract class. Di akhir, kamu akan bisa membaca dan menulis class bertipe dengan tenang.
Kalau class sendiri masih terasa baru buat kamu, ada baiknya membaca Class JavaScript dulu. Semua di sini berasumsi kamu sudah paham bentuk dasar sebuah class.
Mulai dari class JavaScript biasa
Ini class JavaScript biasa yang mewakili seorang pengguna. Belum ada yang istimewa.
class User {
constructor(name, email) {
this.name = name;
this.email = email;
}
greeting() {
return `Halo, saya ${this.name}`;
}
}
const john = new User("John Doe", "john@example.com");
Kode ini jalan, tapi JavaScript dengan santai membiarkan kamu melakukan hal-hal aneh. Kamu bisa menulis new User(42, true) dan tidak ada yang protes sampai sesuatu rusak belakangan. Kamu bisa salah ketik john.emial dan yang kamu dapat hanyalah undefined, bukan pesan error. Di sinilah TypeScript masuk untuk menutup celah-celah itu.
Field yang bertipe
Hal pertama yang ditambahkan TypeScript adalah kemampuan mendeklarasikan field beserta tipenya. Kamu daftarkan setiap properti di bagian atas class: tulis namanya, titik dua, lalu tipe yang dipegangnya.
class User {
name: string;
email: string;
constructor(name: string, email: string) {
this.name = name;
this.email = email;
}
greeting(): string {
return `Halo, saya ${this.name}`;
}
}
const john = new User("John Doe", "john@example.com");
Ada dua hal yang berubah. Field name dan email sekarang dideklarasikan bertipe string di bagian atas, dan parameter constructor pun ikut diberi tipe. Method greeting() juga menyatakan bahwa ia mengembalikan string. Sekarang kalau kamu memanggil new User(42, true), TypeScript langsung menghentikanmu dan menjelaskan bahwa 42 bukan string.
Deklarasikan field, baru isi nilainya
Di TypeScript, biasanya kamu mendaftar semua field di bagian atas class lengkap dengan tipenya. Constructor tetap yang mengisi nilainya, tapi deklarasi itulah yang memberitahu TypeScript bentuk tiap objek. Anggap saja itu label di kotak sebelum kamu mengisi isinya.
Kamu juga bisa memberi field nilai awal, dan TypeScript akan menyimpulkan tipenya dari nilai itu:
class User {
name: string = "Anonymous";
isActive: boolean = true;
}
Di sini kamu bahkan tidak perlu menulis tipenya — TypeScript membaca "Anonymous" dan tahu name itu string, lalu membaca true dan tahu isActive itu boolean.
Access modifier: public, private, protected
Di JavaScript biasa, setiap properti pada objek bisa dijangkau dari mana saja. Kadang itu tidak masalah. Tapi sering kali iya — sebagian detail adalah “isi dapur” yang sifatnya internal, dan kamu tidak ingin kode lain mengutak-atiknya. TypeScript memberi tiga kata kunci untuk mengatur siapa yang boleh menyentuh sebuah field atau method.
| Modifier | Siapa yang boleh mengaksesnya |
|---|---|
public |
Siapa pun, dari mana saja (ini bawaannya) |
private |
Hanya kode di dalam class yang sama |
protected |
Kode di dalam class dan class mana pun yang mewarisinya |
Berikut class User yang sama dengan tambahan field private untuk hash password, yang seharusnya tidak pernah dibaca langsung oleh dunia luar.
class User {
public name: string;
private passwordHash: string;
constructor(name: string, passwordHash: string) {
this.name = name;
this.passwordHash = passwordHash;
}
public checkPassword(hash: string): boolean {
return this.passwordHash === hash;
}
}
const jane = new User("Jane Doe", "a1b2c3");
jane.name; // OK — public
jane.checkPassword("x"); // OK — method public
jane.passwordHash; // Error — passwordHash bersifat private
Baris terakhir itulah intinya. Di luar class, passwordHash seolah-olah tidak ada di mata TypeScript. Kamu dipaksa lewat checkPassword, yaitu pintu terkontrol yang memang kamu inginkan.
protected posisinya di tengah. Anggota protected tersembunyi dari dunia luar, tapi class turunannya masih boleh memakainya.
class Account {
protected balance: number = 0;
}
class SavingsAccount extends Account {
addInterest(rate: number): void {
this.balance += this.balance * rate; // boleh: class turunan bisa melihat protected
}
}
Pemeriksaan ini hanya saat menulis kode
private dan protected ditegakkan oleh TypeScript saat kamu menulis kode, bukan oleh browser saat program berjalan. Setelah TypeScript dikompilasi menjadi JavaScript, field itu kembali menjadi properti biasa. Kalau kamu butuh sebuah nilai benar-benar tersembunyi bahkan saat runtime, pakai field #private bawaan JavaScript. Untuk kode aplikasi sehari-hari, modifier TypeScript biasanya sudah cukup.
readonly: diisi sekali, tidak bisa diubah
Sebagian nilai sebaiknya dikunci begitu objek dibuat — sebuah ID, tanggal pembuatan, hal-hal yang memang tidak semestinya berubah lagi. Kata kunci readonly melakukan itu. Kamu boleh mengisi field tersebut di deklarasi atau di constructor, tapi tidak di tempat lain.
class User {
readonly id: string;
name: string;
constructor(id: string, name: string) {
this.id = id; // OK — diisi di dalam constructor
this.name = name;
}
}
const john = new User("u-001", "John Doe");
john.name = "Johnny"; // OK
john.id = "u-002"; // Error — id bersifat readonly
readonly berbicara soal niat. Ia memberitahu siapa pun yang membaca kodemu, juga TypeScript sendiri, bahwa nilai ini sudah pasti. Mencoba menimpanya akan ketahuan sebelum program berjalan.
Parameter property: jalan pintas yang praktis
Coba lihat lagi class User yang bertipe tadi. Ada pola yang berulang: deklarasikan field, terima parameter dengan nama yang sama, lalu salin parameter itu ke field. TypeScript menyediakan jalan pintas yang menggabungkan ketiga langkah itu menjadi satu. Kalau kamu menaruh access modifier (atau readonly) langsung di depan parameter constructor, TypeScript membuat field-nya dan mengisinya untukmu.
class User {
constructor(
public name: string,
private email: string,
readonly id: string
) {}
greeting(): string {
return `Halo, saya ${this.name}`;
}
}
const jane = new User("Jane Doe", "jane@example.com", "u-002");
Class mungil itu setara dengan mendeklarasikan tiga field lalu mengisi masing-masing di dalam constructor. Di sini badan constructor-nya bahkan kosong karena memang tidak ada lagi yang perlu dikerjakan. Pola ini disebut parameter property, dan kamu akan sering menjumpainya di kode TypeScript nyata.
Modifier-lah yang menjadikannya field
Sebuah parameter constructor baru menjadi field class kalau ada modifier di depannya — public, private, protected, atau readonly. Tanpa modifier, constructor(name: string) hanyalah parameter biasa yang hilang begitu constructor selesai.
Mengimplementasikan interface
Interface adalah deskripsi sebuah bentuk: daftar properti dan method yang harus dimiliki sesuatu, tanpa kode nyata di dalamnya. Class bisa berjanji mengikuti sebuah interface lewat kata kunci implements. TypeScript lalu memeriksa apakah class itu benar-benar menyediakan semua yang dituntut interface.
interface Greetable {
name: string;
greeting(): string;
}
class User implements Greetable {
constructor(public name: string) {}
greeting(): string {
return `Halo, saya ${this.name}`;
}
}
Kalau kamu lupa menulis method greeting, atau salah ketik namanya, TypeScript menolak mengompilasi dan memberitahu bahwa class-mu tidak cocok dengan Greetable. Ini sangat membantu menjaga tim tetap konsisten: sepakati satu interface, dan setiap class yang mengimplementasikannya dijamin punya bentuk yang sama.
Satu class boleh mengimplementasikan lebih dari satu interface — cukup pisahkan dengan koma: class User implements Greetable, Serializable {}.
Abstract class, sekilas
Kadang kamu ingin sebuah base class yang tidak boleh dibuat sendirian, hanya boleh diwarisi. abstract class adalah persis itu — cetakan yang belum lengkap. Ia bisa berisi method yang sudah jadi, tapi ia juga bisa mendeklarasikan method abstract yang tanpa badan, sehingga memaksa setiap class turunan mengisinya.
abstract class Shape {
abstract area(): number; // tanpa badan — turunan wajib menyediakannya
describe(): string {
return `Bentuk ini punya luas ${this.area()}`;
}
}
class Circle extends Shape {
constructor(private radius: number) {
super();
}
area(): number {
return Math.PI * this.radius ** 2;
}
}
const c = new Circle(5);
c.describe(); // "Bentuk ini punya luas 78.53..."
new Shape(); // Error — tidak bisa membuat instance dari abstract class
Shape memberimu method describe() bersama secara gratis, tapi ia menolak dibangun langsung dan mewajibkan setiap turunan mendefinisikan area(). Itulah idenya: perilaku bersama di atas, lubang wajib yang harus diisi oleh anak-anaknya.
Rangkuman singkat
TypeScript tidak menciptakan ulang class — ia menambahkan lapisan keamanan di atas class JavaScript yang mungkin sudah kamu kenal. Ini versi ringkas dari semua yang kita bahas:
- Field bertipe mendeklarasikan tipe tiap properti, jadi nilai yang salah ketahuan lebih awal.
- Access modifier (
public,private,protected) mengatur siapa yang boleh menjangkau sebuah field atau method. readonlymengunci sebuah nilai setelah diisi, biasanya di constructor.- Parameter property membiarkanmu mendeklarasikan sekaligus mengisi field langsung dari parameter constructor dengan menambahkan modifier di depannya.
implementsmembuat sebuah class berjanji cocok dengan sebuah interface, dan TypeScript memverifikasinya.- Abstract class adalah cetakan dasar yang tidak bisa di-instansiasi dan bisa memaksa turunannya mengimplementasikan method tertentu.
Tidak satu pun dari ini mengubah cara kodemu berjalan di browser — semua pemeriksaan terjadi saat kamu menulis, lalu lebur menjadi JavaScript biasa. Imbalannya, satu kategori bug bisa ketahuan sebelum kamu sempat menekan refresh. Mulailah dengan menambahkan tipe ke field-mu, lalu pakai sisanya seiring kebutuhan.