TypeScript 5.5 satisfies operator: Type-safe mà vẫn linh hoạt
Tìm hiểu sức mạnh của satisfies operator trong TypeScript — cách giữ type inference chính xác trong khi vẫn đảm bảo type-safety.
satisfies operator được giới thiệu từ TypeScript 4.9, nhưng đến 5.5 mới thực sự phát huy hết sức mạnh khi kết hợp với các type narrowing improvements. Nếu bạn chưa dùng, bạn đang bỏ lỡ một trong những features hữu ích nhất của TS.
Vấn đề: as const vs type annotation
Giả sử bạn có config object:
type Route = {
path: string;
method: "GET" | "POST" | "PUT" | "DELETE";
auth: boolean;
};
type Routes = Record<string, Route>;
// Cách 1: Type annotation — mất literal types
const routes: Routes = {
home: { path: "/", method: "GET", auth: false },
createPost: { path: "/posts", method: "POST", auth: true },
};
routes.home.method; // type: "GET" | "POST" | "PUT" | "DELETE" 😢
// Bạn BIẾT nó là "GET" nhưng TS không biết
// Cách 2: as const — type-safe nhưng không validate structure
const routes2 = {
home: { path: "/", method: "GET", auth: false },
createPost: { path: "/posts", method: "POSTT", auth: true }, // Typo! Không báo lỗi
} as const;
Cả 2 cách đều có trade-off. satisfies giải quyết cả hai.
satisfies: Best of both worlds
const routes = {
home: { path: "/", method: "GET", auth: false },
createPost: { path: "/posts", method: "POST", auth: true },
// ❌ Nếu thêm method: "POSTT" → TypeScript báo lỗi ngay!
} satisfies Routes;
routes.home.method; // type: "GET" ✅ (giữ literal type)
routes.createPost.auth; // type: true ✅ (giữ literal type)
// Bonus: autocomplete cho keys
routes.home; // ✅ IntelliSense biết "home" và "createPost" là valid keys
satisfies validate rằng value conform to type, nhưng không widen type. Bạn được cả type-safety lẫn precise inference.
Use cases thực tế
1. Theme/Design tokens
type ColorValue = string | { light: string; dark: string };
type Theme = Record<string, ColorValue>;
const theme = {
primary: { light: "#3b82f6", dark: "#60a5fa" },
background: "#ffffff",
danger: { light: "#ef4444", dark: "#f87171" },
} satisfies Theme;
// TS biết chính xác: theme.primary là object, theme.background là string
theme.primary.light; // ✅ OK
theme.background.light; // ❌ Error — đúng rồi, nó là string
2. Event handlers map
type EventMap = Record<string, (...args: never[]) => void>;
const handlers = {
onClick: (e: MouseEvent) => console.log(e.clientX),
onKeyDown: (e: KeyboardEvent) => console.log(e.key),
} satisfies EventMap;
// Parameter types được giữ nguyên
handlers.onClick; // (e: MouseEvent) => void ✅
3. API response mapping
Khi bạn map API endpoints với response types, satisfies đảm bảo mọi endpoint đều conform to schema mà vẫn giữ specific return types cho từng endpoint.
Kết hợp với as const
Trong TypeScript 5.5, bạn có thể kết hợp cả hai:
const routes = {
home: { path: "/", method: "GET", auth: false },
} as const satisfies Routes;
// Giờ routes là fully readonly VÀ validated against Routes type
Khi nào KHÔNG dùng satisfies
- Khi bạn thực sự muốn widen type (ví dụ function parameter)
- Khi literal types không quan trọng — annotation thường đơn giản hơn
- Khi object sẽ được mutate và bạn cần wider types
satisfies là một trong những additions mà khi quen rồi, bạn sẽ dùng ở khắp nơi. Nó giải quyết elegant một vấn đề mà trước đây phải workaround bằng generics phức tạp hoặc type assertions.
Admin
Bình luận (0)
Đăng nhập để bình luận
Chưa có bình luận nào. Hãy là người đầu tiên!