Typing Props di React
Anotasi props komponen React: interface Props, children, dan default props.
Kenapa Typing Props Penting
Di React tanpa TypeScript, props adalah kotak hitam: komponen menerima object, dan kamu baru tahu ada yang kurang saat UI-nya rusak di browser. Dengan props yang di-type, kesalahan seperti lupa mengirim prop wajib atau salah tipe ketahuan saat kamu menulis JSX, lengkap dengan nama prop yang bermasalah. Untuk tim, interface props juga berfungsi sebagai dokumentasi hidup: cukup baca interface-nya untuk tahu cara memakai komponen tanpa membuka implementasinya.
Kapan Dipakai
Setiap komponen yang kamu tulis, tanpa kecuali. Pola standarnya: satu interface XxxProps per komponen, didefinisikan tepat di atas komponennya. Prop wajib tanpa tanda tanya, prop opsional dengan ?, nilai terbatas dengan literal union, dan handler dengan tipe fungsi eksplisit.
Contoh 1: Komponen UserCard yang Realistis
import type { ReactNode } from "react";
interface UserCardProps {
id: string;
nama: string;
peran: "admin" | "kasir" | "pelanggan";
avatarUrl?: string;
onPilih: (id: string) => void;
children?: ReactNode;
}
function UserCard({ id, nama, peran, avatarUrl, onPilih, children }: UserCardProps) {
return (
<div className="card" onClick={() => onPilih(id)}>
{avatarUrl ? (
<img src={avatarUrl} alt={nama} />
) : (
<div className="avatar-fallback">{nama[0]}</div>
)}
<h3>{nama}</h3>
<span className={`badge ${peran}`}>{peran}</span>
{children}
</div>
);
}Perhatikan polanya: prop wajib (id, nama, peran, onPilih) tanpa tanda tanya, prop opsional (avatarUrl, children) dengan ?, nilai terbatas (peran) dengan literal union sehingga typo seperti "admni" langsung ditolak, dan handler (onPilih) dengan tipe fungsi eksplisit agar pemanggil tahu argumen apa yang akan diterima.
Contoh 2: Error Compiler Nyata di JSX
// Salah nilai literal:
<UserCard id="u1" nama="Budi" peran="superadmin" onPilih={(id) => console.log(id)} />
// Error: Type '"superadmin"' is not assignable to type
// '"admin" | "kasir" | "pelanggan"'. (TS2322)
// Lupa prop wajib:
<UserCard id="u1" nama="Budi" peran="kasir" />
// Error: Property 'onPilih' is missing in type
// '{ id: string; nama: string; peran: "kasir"; }' but required
// in type 'UserCardProps'. (TS2741)Dua error paling umum di JSX. TS2322 bilang nilainya salah, TS2741 bilang ada prop wajib yang kurang. Keduanya menunjuk nama prop persisnya, jadi perbaikannya langsung jelas tanpa menebak-nebak.
Contoh 3: Default Props yang Type-Safe
interface TombolProps {
label: string;
varian?: "primer" | "sekunder";
onKlik: () => void;
}
function Tombol({ label, varian = "primer", onKlik }: TombolProps) {
return (
<button className={`btn ${varian}`} onClick={onKlik}>
{label}
</button>
);
}
// <Tombol label="Simpan" onKlik={simpan} /> varian = "primer"
// <Tombol label="Hapus" varian="sekunder" onKlik={h} /> varian = "sekunder"Destructuring dengan nilai default adalah cara modern menggantikan defaultProps (yang sudah deprecated untuk function component). TypeScript memahami bahwa di dalam komponen, varian bertipe "primer" | "sekunder" murni, bukan undefined lagi, karena default-nya selalu mengisi.
Catatan teknis: Jangan pakai
React.FCdi kode baru. Selain diam-diam menambahchildrenopsional ke semua komponen, ia juga merusak inference pada komponen generic. Interface props eksplisit seperti di atas adalah rekomendasi komunitas modern dan dokumentasi resmi React.
Tantangan
Komponen Badge
Buat interface BadgeProps { teks: string; warna?: "merah" | "hijau" | "biru"; onTutup?: () => void } dan komponen Badge yang me-render span dengan class warna dan tombol tutup (×) hanya bila onTutup ada.
interface BadgeProps {
teks: string;
warna?: "merah" | "hijau" | "biru";
onTutup?: () => void;
}
function Badge({ teks, warna = "biru", onTutup }: BadgeProps) {
// render span + tombol tutup kondisional
}