classoopMahir3 mnt baca

Class Dasar di TypeScript

Class dengan anotasi properti, constructor shorthand, dan method bertipe.

Kenapa class masih dipakai di dunia TypeScript

Di frontend React modern, class memang jarang terlihat. Tapi di backend (NestJS), ORM (TypeORM), dan domain logic yang butuh enkapsulasi kuat, class adalah warga kelas satu: dependency injection butuh class sebagai token, entity database dimodelkan sebagai class, dan invariant bisnis ("saldo tidak boleh negatif") paling enak dikunci di dalam method. TypeScript membuat class JavaScript jauh lebih aman lewat anotasi properti, modifier akses, dan constructor shorthand.

Class domain yang realistis: Rekening

typescript
class Rekening {
  readonly id: string;    // tidak bisa diubah setelah dibuat
  private saldo: number;  // hanya bisa diakses dari dalam class

  // Constructor shorthand: "public" otomatis membuat properti.
  constructor(id: string, public pemilik: string, saldoAwal = 0) {
    this.id = id;
    this.saldo = saldoAwal;
  }

  setor(jumlah: number): void {
    if (jumlah <= 0) throw new Error("jumlah harus positif");
    this.saldo += jumlah;
  }

  tarik(jumlah: number): number {
    if (jumlah > this.saldo) throw new Error("saldo tidak cukup");
    this.saldo -= jumlah;
    return jumlah;
  }

  cekSaldo(): number {
    return this.saldo;
  }
}

const rek = new Rekening("R-001", "Budi", 50000);
rek.setor(10000);
console.log(rek.cekSaldo()); // 60000
console.log(rek.pemilik);    // "Budi", public boleh dibaca

Invariant "saldo tidak boleh negatif" terkunci di dalam tarik(). Tidak ada kode luar yang bisa mengakali lewat assignment langsung, karena saldo itu private. Ini inti OOP yang sebenarnya: bukan sekadar grouping fungsi, tapi melindungi state.

Error compiler nyata, tiga yang paling sering

1. Properti tanpa inisialisasi (strict mode):

typescript
class Kucing {
  nama: string; // ERROR
  constructor() {}
}
text
error TS2564: Property 'nama' has no initializer and is not definitely
  assigned in the constructor.

Strict mode memaksa setiap properti punya nilai awal atau diisi di constructor. Cara betulkan: beri nilai default (nama = ""), isi di constructor, atau tandai nama!: string (definite assignment assertion) kalau kamu yakin diisi lewat jalur lain seperti decorator ORM.

2. Menulis ke properti readonly:

typescript
rek.id = "R-999";
text
error TS2542: Cannot assign to 'id' because it is a read-only property.

readonly cocok untuk identifier dan config yang ditetapkan sekali saat konstruksi dan tidak boleh berubah selamanya.

3. Mengakses private dari luar:

typescript
console.log(rek.saldo);
text
error TS2341: Property 'saldo' is private and only accessible
  within class 'Rekening'.

Inilah enkapsulasi yang ditegakkan compiler, bukan sekadar konvensi penamaan underscore yang bisa dilanggar siapa saja.

Kapan dipakai di project nyata

  • NestJS dan backend ber-DI: service dan controller ditulis sebagai class, dependensi di-inject lewat constructor. Tanpa class, framework-nya tidak bisa memetakan dependensi.
  • Entity ORM: class User { @Column() nama: string } di TypeORM. Decorator butuh target berupa class untuk menempelkan metadata.
  • Domain logic ber-invariant: keranjang belanja, rekening bank, mesin status pesanan. Aturan bisnis dikunci di method, state dijaga private, dunia luar hanya boleh berinteraksi lewat API publik class tersebut.

Catatan teknis: private TypeScript hanya dicek saat compile; saat runtime propertinya masih bisa diakses (berbeda dengan #saldo milik JavaScript yang benar-benar privat di runtime). Untuk rahasia yang tidak boleh bocor ke kode lain dalam satu proses (misalnya token di dalam library), pakai # privat JavaScript. Untuk enkapsulasi desain sehari-hari, private TypeScript sudah lebih dari cukup.

Tantangan

Class AkunBank

Buat class AkunBank dengan constructor shorthand: public pemilik: string, private saldo: number = 0. Method: setor(j: number): void, tarik(j: number): boolean (gagal bila saldo kurang), cekSaldo(): number. Pastikan tarik tidak bisa membuat saldo negatif.

typescript
class AkunBank {
  constructor(
    public pemilik: string,
    private saldo: number = 0
  ) {}

  setor(j: number): void {
    // ...
  }

  tarik(j: number): boolean {
    // return false bila kurang
    return true;
  }

  cekSaldo(): number {
    return this.saldo;
  }
}

const a = new AkunBank("Budi", 100000);
a.tarik(150000);
console.log(a.cekSaldo()); // tetap 100000