You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现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',但:

  1. 只有contactPreference的类型是'email'|'phone',其他属性的类型(比如id是string)不会触发分支逻辑,最终返回{}
  2. 交叉类型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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 10:13:15