
TypeScript satisfies operator (toán tử satisfies) cho phép kiểm tra kiểu của một biểu thức phù hợp với kiểu mong đợi mà không thay đổi kiểu suy luận gốc của biểu thức đó. satisfies ra mắt từ TypeScript 4.9, giải quyết bài toán lâu đời: ép kiểu (as) làm mất thông tin kiểu chi tiết, trong khi annotation kiểu tường minh (const x: Type) lại ép buộc kiểu rộng hơn.
Khi bạn viết const config = { url: "https://api.example.com", timeout: 5000 } satisfies AppConfig, TypeScript kiểm tra config có đầy đủ các trường bắt buộc của AppConfig, nhưng kiểu của config vẫn là { readonly url: "https://api.example.com"; readonly timeout: 5000 } — giữ nguyên literal types thay vì rộng thành string và number.

Vấn đề trước satisfies
Giả sử bạn định nghĩa kiểu cấu hình:
interface AppConfig {
url: string;
timeout: number;
retries?: number;
}
Với type annotation:
const config: AppConfig = {
url: "https://api.example.com",
timeout: 5000
};
// Kiểu config: AppConfig (url: string, timeout: number)
// Mất literal types, không biết url chính xác là gì
Với type assertion (as):
const config = {
url: "https://api.example.com",
timeout: 5000
} as AppConfig;
// Kiểu config: { url: string; timeout: number; }
// Cũng mất literal types, và không kiểm tra dư trường
Cả hai cách đều làm mất thông tin literal type quan trọng cho type-level programming, exhaustiveness checking, và autocompletion chính xác.
Cú pháp và hành vi của satisfies
expression satisfies Type
Quy tắc:
- Expression phải phù hợp (assignable to) Type — nếu không, TypeScript báo lỗi.
- Kiểu kết quả của expression giữ nguyên (không widened thành Type).
- Không tạo ra giá trị runtime mới — satisfies bị xóa hoàn toàn khi compile.
Ví dụ thực tế
1. Giữ literal types cho object config
interface ThemeConfig {
colors: {
primary: string;
secondary: string;
};
spacing: number[];
}
const theme = {
colors: {
primary: "#0066cc",
secondary: "#f0f0f0"
},
spacing: [4, 8, 16, 24]
} satisfies ThemeConfig;
// Kiểu theme.colors.primary: "#0066cc" (literal)
// Kiểu theme.spacing: readonly [4, 8, 16, 24] (tuple readonly)
Khi dùng theme.colors.primary trong code, TypeScript biết chính xác giá trị là "#0066cc", cho phép so sánh type-level hay dùng làm key object type-safe.
2. Kiểm tra exhaustiveness cho union type
type Status = "loading" | "success" | "error";
const messages = {
loading: "Đang tải...",
success: "Thành công!",
error: "Có lỗi xảy ra"
} satisfies Record;
// Nếu thêm "pending" vào Status nhưng quên cập nhật messages,
// TypeScript báo lỗi: Property 'pending' is missing
Đây là pattern phổ biến để đảm bảo mapping hoàn chỉnh giữa union type và object.
3. Kiểm tra dư trường (excess property checking)
interface User {
name: string;
email: string;
}
const user = {
name: "Nguyễn Văn A",
email: "[email protected]",
age: 25 // Lỗi: 'age' does not exist in type 'User'
} satisfies User;
Khác với as User (chỉ widen, không kiểm tra dư), satisfies báo lỗi trường thừa — giúp bắt lỗi chính tả key object.

Khi nào dùng satisfies thay vì as hoặc annotation?
| Trường hợp | Khuyên dùng | Lý do |
|---|---|---|
| Config object cần literal types | satisfies | Giữ literal, kiểm tra shape |
| Mapping union → object | satisfies Record | Exhaustiveness checking |
| Callback handler type-safe | satisfies | Giữ параметр types chính xác |
| Cần ép kiểu rộng hơn (upcast) | annotation (: Type) | satisfies không widen |
| Biết chắc expression đúng kiểu, muốn tắt check | as Type | satisfies vẫn check |
Các cạm bẫy thường gặp
1. satisfies không widen — đôi khi bạn CẦN widen
const values = [1, 2, 3] satisfies number[];
// Kiểu: readonly [1, 2, 3] (tuple readonly)
// Không thể push: values.push(4) // Error
// Cần widen thì dùng annotation:
const values: number[] = [1, 2, 3];
// Kiểu: number[], có thể push
2. satisfies với function expression
type Handler = (x: number) => string;
const handler = (x: number) => x.toString() satisfies Handler;
// OK, nhưng kiểu handler vẫn là (x: number) => string
// Không khác gì annotation trong trường hợp này
3. satisfies không làm deep partial
interface DeepConfig {
database: {
host: string;
port: number;
};
}
const config = {
database: {
host: "localhost"
// thiếu port
}
} satisfies DeepConfig;
// Lỗi: Property 'port' is missing in type '{ host: string }'
// satisfies kiểm tra deep structure
Kết hợp với const assertions
as const khiến object thành readonly và literal types toàn bộ. satisfies bổ sung kiểm tra shape:
const rawConfig = {
api: {
baseUrl: "https://api.example.com",
version: "v1"
},
features: ["auth", "billing", "analytics"]
} as const;
// Kiểm tra shape mà không mất readonly/literal:
const config = rawConfig satisfies {
api: { baseUrl: string; version: string };
features: readonly string[];
};
Hiệu năng compile-time
satisfies không tăng chi phí type-checking đáng kể. Nó thực chất là một type guard ở mức biểu thức, tương tự như kiểm tra assignability trong annotation nhưng bảo toàn kiểu gốc. Trong project lớn, satisfies giúp giảm dùng as không an toàn và annotation quá rộng, cải thiện type inference toàn cục.
Kết luận
TypeScript satisfies operator là công cụ tinh tế cho type safety: nó kiểm tra “đúng hình dạng” mà không ép “đúng kiểu rộng”. Dùng satisfies khi bạn muốn giữ literal types, bắt lỗi trường thừa, đảm bảo exhaustiveness mapping — những tình huống mà as quá lỏng và : Type quá chặt. Đây là minh chứng cho triết lý TypeScript: kiểu tĩnh phục vụ developer, không cản trở suy luận tự nhiên của ngôn ngữ.
Nguồn tham khảo: TypeScript 4.9 Release Notes – satisfies, PR gốc introduces satisfies
