implements
Pastikan class memenuhi kontrak interface dengan implements.
Kenapa implements itu ada
extends mewarisi implementasi dari satu parent class. implements beda: ia berkata "class ini WAJIB memenuhi kontrak interface ini", tanpa mewarisi kode apa pun. Ini fondasi pola adapter dan strategy: banyak class boleh punya implementasi yang beda-beda, tapi semuanya dijamin punya bentuk yang sama. Kode yang memakai mereka cukup pegang interface-nya, tidak peduli class konkret apa di baliknya.
Skenario nyata: payment gateway ganda
Sebuah toko online mau mendukung dua payment gateway. Kontraknya cukup satu:
// src/payment/gateway.ts
export interface PaymentGateway {
charge(jumlah: number, orderId: string): Promise<string>; // return: transaction id
refund(transactionId: string): Promise<void>;
}// src/payment/stripe-adapter.ts
import type { PaymentGateway } from "./gateway.js";
export class StripeAdapter implements PaymentGateway {
async charge(jumlah: number, orderId: string): Promise<string> {
// panggil Stripe API di sini
return `stripe-tx-${orderId}`;
}
async refund(transactionId: string): Promise<void> {
// panggil Stripe refund di sini
}
}// src/payment/doku-adapter.ts
import type { PaymentGateway } from "./gateway.js";
export class DokuAdapter implements PaymentGateway {
async charge(jumlah: number, orderId: string): Promise<string> {
// panggil Doku API di sini
return `doku-tx-${orderId}`;
}
async refund(transactionId: string): Promise<void> {
// panggil Doku refund di sini
}
}// src/checkout.ts
import type { PaymentGateway } from "./payment/gateway.js";
export async function bayar(
gateway: PaymentGateway,
jumlah: number,
orderId: string
): Promise<string> {
return gateway.charge(jumlah, orderId);
}
// bayar(new StripeAdapter(), ...) atau bayar(new DokuAdapter(), ...): sama saja.Fungsi bayar tidak peduli gateway apa yang dipakai. Mau tambah gateway ketiga besok? Buat class baru yang implements PaymentGateway, tidak ada satu baris pun kode lama yang perlu diubah.
Error compiler nyata
Lupa mengimplementasikan satu method:
export class OvoAdapter implements PaymentGateway {
async charge(jumlah: number, orderId: string): Promise<string> {
return `ovo-tx-${orderId}`;
}
// refund() ketinggalan!
}error TS2420: Class 'OvoAdapter' incorrectly implements interface 'PaymentGateway'.
Property 'refund' is missing in type 'OvoAdapter' but required in type 'PaymentGateway'.Salah tanda tangan method juga langsung ketahuan:
export class RusakAdapter implements PaymentGateway {
// charge versi sync, bukan Promise: melanggar kontrak
charge(jumlah: number, orderId: string): string { return "x"; }
async refund(transactionId: string): Promise<void> {}
}error TS2416: Property 'charge' in type 'RusakAdapter' is not assignable
to the same property in base type 'PaymentGateway'.Compiler berperan sebagai satpam kontrak: tidak ada adapter setengah jadi yang lolos diam-diam ke production.
implements vs extends, jangan tertukar
extends | implements | |
|---|---|---|
| Jumlah | satu parent class saja | boleh banyak interface |
| Mewarisi kode | ya, kode parent dipakai ulang | tidak, hanya cek bentuk |
| Dipakai untuk | hierarki "adalah" (Admin extends User) | kontrak "bisa" (bisa di-charge, bisa di-refund) |
Satu class boleh extends satu base class SEKALIGUS implements beberapa interface. Itu kombo paling umum di framework: class UserService extends BaseService implements OnModuleInit.
Kapan dipakai di project nyata
- Adapter pihak ketiga: payment, SMS, email, cloud storage. Interface milikmu, implementasi mengikuti API mereka. Ganti vendor tanpa mengubah business logic.
- Repository pattern:
interface UserRepositorydenganPostgresUserRepountuk production danInMemoryUserRepountuk test. Test jadi cepat tanpa butuh database beneran. - Plugin dan strategy: sistem diskon (
DiskonPersen implements StrategiDiskon,DiskonNominal implements StrategiDiskon), exporter laporan (PDF, Excel), notifier (WhatsApp, email). Tambah varian baru tanpa menyentuh kode lama.
Catatan teknis:
implementshanya dicek saat compile dan hilang total saat runtime. Jadi jangan pakaiinstanceof PaymentGatewayuntuk pengecekan, karena interface tidak punya wujud di JavaScript hasil compile. Kalau butuh cek runtime, pakai class konkretnya (x instanceof StripeAdapter) atau type guard manual.
Tantangan
Kontrak penyimpanan
Buat interface Penyimpanan dengan simpan(kunci: string, nilai: string): void dan ambil(kunci: string): string | undefined. Lalu class Memori implements Penyimpanan memakai Map di dalamnya. Test simpan dan ambil.
interface Penyimpanan {
simpan(kunci: string, nilai: string): void;
ambil(kunci: string): string | undefined;
}
class Memori implements Penyimpanan {
private data = new Map<string, string>();
// lengkapi kedua method
}
const m = new Memori();
m.simpan("nama", "Budi");
console.log(m.ambil("nama")); // "Budi"