如何实现TypeScript工具类型ConditionalRequired:基于属性值设必填项
实现条件性强制对象属性必填的TypeScript工具类型
我需要定义一个TypeScript工具类型ConditionalRequired,根据对象中contactPreference属性的值,条件性强制对应的联系方式属性为必填项。现有类型定义如下:
export type User = { id: string; name: string; email?: string; phoneNumber?: string; contactPreference: 'email' | 'phone'; };
要求:
- 当
contactPreference为email时,email必须为必填项 - 当
contactPreference为phone时,phoneNumber必须为必填项
正确实现方式
通过联合类型拆分两种场景,分别对每种场景下的必填属性做强制处理:
type ConditionalRequired<T extends { contactPreference: 'email' | 'phone' }> = | (Omit<T, 'email'> & { contactPreference: 'email'; email: NonNullable<T['email']> }) | (Omit<T, 'phoneNumber'> & { contactPreference: 'phone'; phoneNumber: NonNullable<T['phoneNumber']> });
原实现失败原因
你之前的代码逻辑存在核心问题:
// 错误实现 type ConditionalRequired<T> = T & { [K in keyof T]: T[K] extends 'email' ? { email: string } : T[K] extends 'phone' ? { phoneNumber: string } : {}; };
这段代码遍历T的所有属性键K,判断属性类型T[K]是否匹配'email'或'phone',但:
- 只有
contactPreference的类型是'email'|'phone',其他属性的类型(比如id是string)不会触发分支逻辑,最终返回{} - 交叉类型
T & {}等价于T,完全没有修改原类型的属性可选性,自然无法实现强制必填的效果
验证示例
使用正确的ConditionalRequired后,以下示例会按预期工作:
// 合法:contactPreference为email,email已填写 const user1: ConditionalRequired<User> = { id: '1', name: 'foo', contactPreference: 'email', email: 'foobar@gmail.com', }; // 类型错误:contactPreference为email,但缺少email属性 const user2: ConditionalRequired<User> = { id: '2', name: 'foo2', contactPreference: 'email', }; // 类型错误:contactPreference为phone,但缺少phoneNumber属性 const user3: ConditionalRequired<User> = { id: '3', name: 'foo3', contactPreference: 'phone', };
内容的提问来源于stack exchange,提问作者Syed Mustaqhim
相关产品推荐
相关产品推荐

