Kumpulan terkurasi berisi pola TypeScript praktis yang meningkatkan safety, keterbacaan, kemudahan pemeliharaan, dan developer experience.
Sebagian besar tips ini tampak kecil bila berdiri sendiri. Namun jika digabungkan, semuanya mengubah secara drastis bagaimana rasanya bekerja dengan kode TypeScript.
Daftar Isi
- Utamakan
unknownDibandingany - Biarkan Type Inference Bekerja
- Utamakan
satisfiesDibandingas - Turunkan Type dari Value
- Modelkan State Mustahil dengan Discriminated Union
- Gunakan Exhaustive Check dengan
never - Gunakan
as constuntuk Konstanta - Gunakan Type Predicate
- Bangun Type dari Type yang Sudah Ada
- Validasi Data Eksternal saat Runtime
- Hindari
enumpada Sebagian Besar Kasus - Utamakan Generic yang Dapat Diinferensi
- Aktifkan Opsi Compiler yang Strict
- Pelajari Template Literal Type
- Type-Safe ≠ Runtime-Safe
Utamakan unknown Dibanding any
Banyak hal soal type safety bermula di sini.
Sebagian besar masalah TypeScript dimulai ketika any masuk ke dalam sistem.
function parse(data: unknown) {
if (typeof data === "string") {
return data.toUpperCase();
}
}
Mengapa ini penting
- Memaksa validasi
- Menjaga safety
- Mencegah kebocoran type (type leakage)
Biarkan Type Inference Bekerja
Kode TypeScript terbaik sering kali memiliki lebih sedikit type eksplisit daripada yang dibayangkan pemula.
const name = "Ada";
Alih-alih:
const name: string = "Ada";
Anotasi berlebihan
- Memperlebar type
- Mengganggu inference
- Menciptakan beban pemeliharaan (maintenance overhead)
Inference cenderung lebih mudah diskalakan dibanding anotasi.
Utamakan satisfies Dibanding as
Salah satu fitur TypeScript modern yang paling penting.
const routes = {
home: "/",
about: "/about",
} satisfies Record<string, string>;
Alih-alih:
const routes = {
home: "/",
about: "/about",
} as Record<string, string>;
satisfies melakukan validasi tanpa kehilangan inference.
Turunkan Type dari Value, Bukan Menduplikasinya
Salah satu pergeseran pola pikir terbesar dalam TypeScript.
const roles = ["admin", "user", "guest"] as const;
type Role = (typeof roles)[number];
Menjaga sinkronisasi antara runtime value dan type secara manual hampir selalu melenceng seiring waktu.
Modelkan State Mustahil dengan Discriminated Union
Di sinilah TypeScript mulai bersifat arsitektural.
type State =
| { status: "loading" }
| { status: "success"; data: User }
| { status: "error"; error: Error };
Model seperti ini cenderung jauh lebih mudah diskalakan dibanding gumpalan property opsional yang longgar.
Gunakan Exhaustive Check dengan never
Discriminated union menjadi jauh lebih ampuh dengan exhaustiveness checking.
default: {
const exhaustive: never = state;
return exhaustive;
}
Refactor di masa depan akan menjadi error compiler, bukan bug runtime.
Gunakan as const untuk Konfigurasi dan Konstanta
Tanpa as const:
const theme = {
mode: "dark",
};
mode menjadi string.
Dengan as const:
const theme = {
mode: "dark",
} as const;
Kini nilainya menjadi 'dark'.
Fitur kecil, manfaat besar.
Gunakan Type Predicate untuk Narrowing yang Dapat Dipakai Ulang
Hubungkan pengecekan runtime dengan kecerdasan compile-time.
function isUser(value: unknown): value is User {
return typeof value === "object" && value !== null && "id" in value;
}
Lalu:
if (isUser(data)) {
data.id;
}
Hal ini menjadi sangat berguna di sekitar API dan batas input eksternal.
Bangun Type Baru dari Type yang Sudah Ada
Berpikirlah dalam kerangka transformasi, bukan duplikasi.
type UserPreview = Pick<User, "id" | "name">;
Pelajari utility type berikut
PickOmitPartialRequired- Indexed access types
Utility ini menjadi jauh lebih bernilai seiring bertumbuhnya aplikasi.
Validasi Data Eksternal saat Runtime
TypeScript tidak memvalidasi response API.
Ini adalah salah satu bagian TypeScript yang paling sering disalahpahami.
const UserSchema = z.object({
id: z.string(),
name: z.string(),
});
Type safety berakhir di batas runtime kecuali Anda melakukan validasi.
Hindari enum pada Sebagian Besar Kasus
Biasanya lebih sederhana:
const roles = ["admin", "user"] as const;
Dibanding:
enum Role {
Admin,
User,
}
Pada sebagian besar aplikasi, literal union pada akhirnya lebih sederhana untuk di-refactor, lebih mudah diserialisasi, dan tidak terlalu mengejutkan saat runtime dibanding enum.
Utamakan Generic yang Dapat Diinferensi Otomatis
API TypeScript yang baik jarang membutuhkan argumen generic manual.
Kurang ideal:
getData<User>();
Lebih baik:
getData(userSchema);
Inference biasanya lebih mudah diskalakan dibanding API yang padat anotasi.
Aktifkan Opsi Compiler yang Strict
Banyak tim menggunakan TypeScript dalam "mode autocomplete".
Strict mode adalah titik ketika TypeScript benar-benar mulai membuahkan hasil.
{
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true
}
Flag-flag ini meningkatkan correctness secara drastis.
Pelajari Template Literal Type
Salah satu fitur TypeScript modern yang paling ampuh.
type Route = `/api/${string}`;
Sangat cocok untuk:
- Route
- Nama event
- Utility CSS
- Design system
- Query key
Begitu Anda mulai menggunakannya, fitur ini akan muncul di mana-mana.
"Type-Safe" Tidak Berarti "Runtime-Safe"
Tips penutup yang sempurna karena membingkai ulang segalanya.
Kode ini berhasil dikompilasi:
const user = (await response.json()) as User;
Tetapi tetap bisa meledak saat runtime.
TypeScript meningkatkan correctness, tetapi:
- Tidak menggantikan validasi
- Tidak menjamin arsitektur yang baik
- Tidak menghilangkan bug runtime
Perbedaan ini menjadi semakin penting pada sistem yang lebih besar.