Ringkasan Utama
Operator satisfies memeriksa bahwa sebuah nilai sesuai dengan sebuah tipe tanpa melebarkan nilai itu menjadi tipe tersebut. Anda tetap memegang tipe literal yang sempit yang diinferensi TypeScript dari apa yang benar-benar Anda tulis, sambil tetap menangkap salah ketik, kunci yang hilang, dan tipe nilai yang salah saat menulis. Posisinya berada di antara anotasi tipe dan cast as.
Selama bertahun-tahun saya menulis objek config di TypeScript dan diam-diam menerima pertukaran yang buruk. Jika saya menganotasi objek dengan sebuah tipe, saya mendapat validasi tapi kehilangan nilai spesifik yang diketahui compiler. Jika saya melewati anotasi, saya menyimpan nilai spesifik tapi tidak mendapat pemeriksaan sama sekali. Operator satisfies, ditambahkan di TypeScript 4.9 pada akhir 2022 dan sekarang menjadi sesuatu yang saya gunakan setiap minggu, melarutkan pertukaran itu. Tulisan ini adalah kumpulan pola yang benar-benar saya pakai untuknya di kode NestJS dan Next.js produksi.
Masalahnya: anotasi melebarkan, cast berbohong
Untuk memahami mengapa satisfies ada, lihat dua alat yang digantikannya. Anotasi tipe memaksa nilai naik untuk cocok dengan tipe yang dideklarasikan, sehingga compiler melupakan kunci dan literal spesifik yang Anda tulis. Assertion tipe dengan as melakukan sebaliknya: ia membungkam compiler sepenuhnya, sehingga salah ketik atau properti yang hilang lolos begitu saja. Tidak satu pun memberi Anda validasi plus presisi sekaligus.
type RouteConfig = Record<string, { path: string; auth: boolean }>;
// Annotation: validated, but the value is WIDENED.
const routesA: RouteConfig = {
home: { path: "/", auth: false },
admin: { path: "/admin", auth: true },
};
routesA.dashboard; // no error — TS forgot the exact keys
// Cast: keeps keys, but silences real errors.
const routesB = {
home: { path: "/", atuh: false }, // typo — NOT caught
} as RouteConfig;Solusinya: nilai mengalahkan tipe
Dengan satisfies, nilai menyimpan tipe inferensinya sendiri dan tipe yang dideklarasikan hanya digunakan sebagai batasan untuk diperiksa. Model mental yang saya pakai: dengan anotasi tipe yang menang, dengan satisfies nilai yang menang. Anda mendapat error jika objek melanggar batasan, tapi tipe variabel tetap persis sesempit apa yang Anda tulis. Itu berarti autocomplete pada kunci sebenarnya dan tidak ada properti hantu.
const routes = {
home: { path: "/", auth: false },
admin: { path: "/admin", auth: true },
} satisfies Record<string, { path: string; auth: boolean }>;
routes.home.path; // string, autocompleted
routes.dashboard; // Error: property 'dashboard' does not exist
// and a typo like `atuh: false` fails right here at author timeAturan satu baris paling jelas yang saya berikan ke rekan tim: gunakan anotasi saat Anda ingin variabel dapat dipakai ulang sebagai tipe lebar, gunakan satisfies saat Anda ingin mengunci bentuk persis yang Anda tulis dan tetap diberi tahu saat ia bergeser. Gunakan as hanya saat Anda benar-benar tahu lebih dari compiler.
Pola 1: objek config dengan tipe nilai campuran
Ini adalah contoh kanonik dari catatan rilis TypeScript 4.9 dan yang meyakinkan saya. Misal sebuah config menyimpan nilai yang kadang string dan kadang tuple. Anotasi ia dan setiap nilai runtuh menjadi union, sehingga Anda tidak bisa lagi memanggil metode string pada entri string tanpa penyempitan. Dengan satisfies batasan tetap diberlakukan, tapi setiap properti menyimpan tipe presisinya dan metodenya tetap tersedia.
type Color = string | [number, number, number];
const palette = {
primary: "#0ea5e9",
danger: [239, 68, 68],
muted: "#64748b",
} satisfies Record<string, Color>;
palette.primary.toUpperCase(); // OK — TS knows it's a string
palette.danger.map((c) => c); // OK — TS knows it's a tuple
// Without satisfies (plain annotation), both lines would error.Pola 2: route map dan cakupan kunci menyeluruh
Ketika kunci harus mencakup union yang diketahui secara persis, satisfies terhadap Record yang dikunci oleh union itu memberi Anda pemeriksaan kelengkapan saat kompilasi. Lupakan satu kunci dan build gagal; tambahkan kunci tak dikenal dan ia juga gagal. Saya memakai ini untuk feature flag, peta izin, dan kamus locale, agar anggota enum baru tidak bisa dirilis setengah terpasang.
type Role = "admin" | "editor" | "viewer";
const permissions = {
admin: ["read", "write", "delete"],
editor: ["read", "write"],
viewer: ["read"],
} satisfies Record<Role, string[]>;
// If you add a fourth Role later and forget it here,
// this line stops compiling — a free exhaustiveness guard.
function can(role: Role): string[] {
return permissions[role];
}Pola 3: satisfies bersama as const
Sendiri, as const membekukan nilai ke tipe literal readonly terdalamnya tapi tidak memberi validasi skema, sehingga salah ketik pada kunci atau nilai bertipe salah tidak terdeteksi saat Anda menulisnya. Gabungkan keduanya dan Anda mendapat keduanya: literal terkunci dan bentuk diperiksa. Urutannya penting. Letakkan satisfies setelah as const agar tipe literal readonly itulah yang divalidasi terhadap batasan.
const endpoints = {
users: "/api/users",
orders: "/api/orders",
} as const satisfies Record<string, `/api/${string}`>;
type Endpoint = typeof endpoints[keyof typeof endpoints];
// Endpoint = "/api/users" | "/api/orders" — exact literals,
// AND every value is verified to match the /api/ template.Jangan membalik urutan menjadi satisfies sebelum as const. Itu memvalidasi objek yang sudah dilebarkan dan mutable terlebih dahulu lalu membekukannya, yang bisa menghilangkan penyempitan literal yang Anda inginkan. Ketika Anda butuh keduanya, urutannya selalu nilai, lalu as const, lalu satisfies.
Kapan TIDAK memakai satisfies
Ini bukan pengganti universal untuk anotasi. Jika sebuah nilai adalah parameter fungsi atau field kelas yang di-assign kode lain sebagai tipe lebar, anotasi ia, karena Anda ingin tipe lebar menjadi kontraknya. Gunakan satisfies untuk nilai terminal yang Anda tulis sekali dan baca berkali-kali: config, peta, tabel konstanta. Dan jangan pernah memakainya untuk menutupi error yang tidak Anda pahami, itu yang digoda as untuk dilakukan dan satisfies sengaja tidak.
| Pendekatan | Memvalidasi bentuk | Menjaga tipe sempit | Terbaik untuk |
|---|---|---|---|
| Anotasi (tipe titik dua) | Ya | Tidak — melebarkan | Variabel, param, field yang dipakai ulang |
| cast as | Tidak — membungkam error | Ya | Pelarian langka saat Anda tahu lebih dari TS |
| satisfies | Ya | Ya | Objek config, peta route/izin |
| as const + satisfies | Ya | Ya — literal + readonly | Tabel konstanta yang butuh literal persis |
Tidak satu pun dari ini butuh toolchain paling mutakhir. Operator ini sudah stabil sejak TypeScript 4.9, jadi proyek apa pun di 5.x atau compiler Go-native 7.0 baru yang dirilis pertengahan 2026 sudah memilikinya. Tidak ada biaya runtime juga, satisfies adalah konstruk murni tingkat-tipe yang terhapus sepenuhnya saat kompilasi, persis seperti anotasi. Ini salah satu kebiasaan dengan leverage tertinggi dan risiko terendah yang saya pungut dalam tahun-tahun TypeScript harian.

