DeepPartial<T> — làm optional đệ quy, nhưng phải dừng đúng chỗ, nếu không nó sẽ đi vào Date, Function và biến chúng thành object rỗng.
type Primitive = string | number | boolean | bigint | symbol | null | undefined
type DeepPartial<T> =
T extends Primitive | Date | Function ? T
: T extends readonly (infer E)[] ? DeepPartial<E>[]
: { [K in keyof T]?: DeepPartial<T[K]> }Thứ tự nhánh quan trọng: kiểm tra primitive và built-in trước khi rơi vào mapped type. Bản ngây thơ { [K in keyof T]?: DeepPartial<T[K]> } áp lên Date sẽ trả về object có các method optional, không còn dùng được như Date.
RequireAtLeastOne<T> — bắt buộc có ít nhất một trong các key chỉ định (hay dùng cho filter API: phải truyền email hoặc phone).
type RequireAtLeastOne<T, K extends keyof T = keyof T> =
Omit<T, K> & { [P in K]-?: Required<Pick<T, P>> & Partial<Omit<T, P>> }[K]
type Query = RequireAtLeastOne<{ email?: string; phone?: string; id?: number }, 'email' | 'phone'>
// { email: string } | { phone: string } | { email: string; phone: string } đều hợp lệ
// {} → errorCơ chế: mapped type sinh một object cho mỗi key, rồi [K] index vào để lấy union các biến thể — mỗi biến thể ép một key thành bắt buộc, các key còn lại optional.
Lưu ý chi phí: type đệ quy sâu làm chậm type checking rõ rệt trên codebase lớn; TypeScript cũng có giới hạn độ sâu instantiation. Chỉ dùng ở nơi thực sự cần, đừng áp DeepPartial cho toàn bộ domain model.