Kiểu điều kiện (Conditional Types) trong TypeScript là một tính năng mạnh mẽ giúp bạn xác định kiểu dựa trên điều kiện. Nó hoạt động giống như biểu thức điều kiện if-else trong ngôn ngữ lập trình, nhưng áp dụng cho các kiểu. Điều này cho phép bạn xây dựng các kiểu phức tạp và linh hoạt, phụ thuộc vào các điều kiện khác nhau.
Cú pháp của Kiểu Điều Kiện
Cú pháp cơ bản của kiểu điều kiện trong TypeScript có dạng như sau:
Trong đó:
- T là kiểu đầu vào.
- U là kiểu mà bạn muốn so sánh với T.
- X là kiểu kết quả nếu T mở rộng (extends) U.
- Y là kiểu kết quả nếu T không mở rộng U.
TypeScript sẽ kiểm tra điều kiện T extends U
:
- Nếu đúng (tức là T mở rộng U), thì kiểu sẽ là X.
- Nếu sai, thì kiểu sẽ là Y.
Ví dụ cơ bản về kiểu điều kiện
Dưới đây là một ví dụ đơn giản sử dụng kiểu điều kiện:
type CheckString<T> = T extends string ? "This is a string" : "This is not a string";
// Áp dụng kiểu điều kiện cho một số kiểu
type A = CheckString<string>; // "This is a string"
type B = CheckString<number>; // "This is not a string"
Trong ví dụ này, kiểu CheckString<T>
kiểm tra xem kiểu T có phải là một chuỗi (string) hay không. Nếu đúng, kết quả sẽ là "This is a string"
, nếu không thì kết quả là "This is not a string"
.
Ứng dụng thực tế của kiểu điều kiện
Kiểu điều kiện trở nên hữu ích khi bạn cần xây dựng các kiểu phụ thuộc vào các điều kiện động, đặc biệt là trong các trường hợp phức tạp khi làm việc với các kiểu dữ liệu khác nhau.
1. Kiểm tra xem kiểu là void
hay không
Bạn có thể sử dụng kiểu điều kiện để kiểm tra xem một kiểu có phải là void
hay không:
type IsVoid<T> = T extends void ? true : false;
type A = IsVoid<void>; // true
type B = IsVoid<string>; // false
2. Loại bỏ các kiểu không mong muốn (Excluding Types)
Bạn có thể sử dụng kiểu điều kiện để loại bỏ các kiểu không mong muốn từ một union type (kiểu hợp):
type ExcludeString<T> = T extends string ? never : T;
type Result = ExcludeString<string | number | boolean>; // Result sẽ là number | boolean
Trong ví dụ trên, kiểu ExcludeString<T>
loại bỏ kiểu string
khỏi union string | number | boolean
và trả về number | boolean
.
3. Kết hợp kiểu điều kiện với các hàm kiểu (Utility Types)
TypeScript cung cấp các utility types được xây dựng sẵn và chúng thường sử dụng kiểu điều kiện để thao tác trên các kiểu dữ liệu. Ví dụ:
Exclude<T, U>
: Loại bỏ các phần tử của T mà mở rộng U.
Extract<T, U>
: Chỉ lấy các phần tử của T mà mở rộng U.
NonNullable<T>
: Loại bỏ null
và undefined
khỏi T.
Ví dụ sử dụng kiểu Extract
:
type ExtractStringOrNumber<T> = Extract<T, string | number>;
type Result = ExtractStringOrNumber<string | boolean | number>; // Result là string | number
Ở đây, kiểu Extract<T, U>
sẽ chỉ giữ lại các kiểu của T mà cũng thuộc về U, trong trường hợp này là string | number
.
4. Kiểu điều kiện đệ quy
Kiểu điều kiện trong TypeScript cũng có thể được sử dụng đệ quy, cho phép xử lý các cấu trúc dữ liệu lồng nhau. Ví dụ:
type UnwrapArray<T> = T extends (infer U)[] ? U : T;
type Result1 = UnwrapArray<string[]>; // string
type Result2 = UnwrapArray<number[][]>; // number[]
Trong ví dụ này, kiểu UnwrapArray<T>
sử dụng infer
để xác định kiểu phần tử của mảng. Nếu T là một mảng, nó sẽ trả về kiểu phần tử bên trong, nếu không nó trả về T.
Kết luận
Kiểu điều kiện trong TypeScript là một tính năng mạnh mẽ giúp bạn xác định các kiểu phụ thuộc vào điều kiện, mang lại tính linh hoạt cao cho các cấu trúc mã phức tạp. Nó cho phép bạn xây dựng các kiểu dựa trên các điều kiện logic, đồng thời kết hợp với các tính năng như extends
, infer
, và các utility types để quản lý kiểu dữ liệu một cách hiệu quả. Nhờ kiểu điều kiện, bạn có thể tạo ra các cấu trúc mã linh hoạt và chặt chẽ, dễ bảo trì trong các ứng dụng TypeScript lớn.