infer khai báo một biến kiểu được suy ra từ vị trí khớp mẫu trong nhánh extends, dùng để rút một phần kiểu ra.
ts
type ElementOf<T> = T extends readonly (infer E)[] ? E : never
type A = ElementOf<string[]> // string
type Unwrap<T> = T extends Promise<infer V> ? Unwrap<V> : T
type B = Unwrap<Promise<Promise<number>>> // number (đệ quy bóc nhiều lớp)Bẫy distributive: khi kiểm tra một naked type parameter với union, conditional type phân phối trên từng nhánh rồi hợp kết quả lại.
ts
type ToArray<T> = T extends any ? T[] : never
type C = ToArray<string | number> // string[] | number[] chứ không phải (string|number)[]
type IsAny<T> = T extends string ? true : false
type D = IsAny<string | number> // boolean (true | false) — dễ hiểu sai là falseHệ quả hay gặp nhất: NonNullable-kiểu tự viết vô tình bỏ nhánh, hoặc T extends never luôn trả về never vì union rỗng không có nhánh nào để phân phối.
Tắt distributive bằng cách bọc cả hai phía trong tuple:
ts
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never
type E = ToArrayNonDist<string | number> // (string | number)[]Quy tắc nhớ: distributive chỉ xảy ra khi bên trái là tham số kiểu trần, chưa bị bọc.