TypeScript动态对象联合键类型设置失效及可选类型疑问
问题描述
我有一个动态设置字段和值的对象:
type PhoneFields = 'deliveryPhoneNumber' | 'pickupPhoneNumber' (props: { phoneField?: PhoneFields }) { const initialValues = { [props.phoneField ?? 'deliveryPhoneNumber']: props.address.phone ?? '' } }
我希望为该动态对象设置类型,使其键只能是联合类型PhoneFields中的某一个,因此定义了如下映射类型:
type PhoneValues = { [x in PhoneFields]: string }
但这样会触发TypeScript错误:
Type '{ [x: string]: string; }' is missing the following properties from type 'PhoneValues': deliveryPhoneNumber, pickupPhoneNumber
更新尝试
根据建议将映射类型的键改为可选后,TypeScript错误消失:
type PhoneValues = { [x in PhoneFields]?: string }
但这种设置无法限制非法键,比如使用非PhoneFields的字符串作为键时不会报错:
const initialValues = { [props.phoneField ?? 'xxxx']: props.address.phone ?? '' }
正确解决方案
你需要的是仅包含PhoneFields中单个键的对象类型,而非包含所有键(可选或必选)的类型,具体实现如下:
方式一:手动声明联合类型
直接定义两种单键对象的联合,清晰直观:
type PhoneValues = | { deliveryPhoneNumber: string; pickupPhoneNumber?: never } | { pickupPhoneNumber: string; deliveryPhoneNumber?: never };
方式二:通用工具类型自动生成
如果联合类型的成员较多,可以用工具类型自动推导,避免重复代码:
// 通用工具类型:生成仅包含联合类型中单个键的对象类型 type SingleKeyObj<T extends string> = { [K in T]: { [key in K]: string; } & { [key in Exclude<T, K>]?: never; }; }[T]; // 生成PhoneValues类型 type PhoneValues = SingleKeyObj<PhoneFields>;
为什么之前的方法不生效?
- 最初的
{ [x in PhoneFields]: string }要求对象必须包含所有PhoneFields的键,但你的动态对象只会有一个键,类型不匹配导致报错。 - 改成可选键后,
{ [x in PhoneFields]?: string }允许对象有0个或多个合法键,但TypeScript对动态键的场景不会触发额外属性检查,因此非法键也能通过类型校验。
验证效果
使用上述PhoneValues类型后:
- 合法动态键会被正确识别:
// 类型匹配,无报错 const initialValues: PhoneValues = { [props.phoneField ?? 'deliveryPhoneNumber']: props.address.phone ?? '' }; - 非法键会触发TypeScript错误:
// 报错:类型"{ xxxx: string; }"不能赋值给类型"PhoneValues" const initialValues: PhoneValues = { [props.phoneField ?? 'xxxx']: props.address.phone ?? '' };
内容的提问来源于stack exchange,提问作者Ludwig
相关产品推荐
相关产品推荐

