通过对象数组中的键推断值类型的TypeScript问题
TypeScript:根据对象键推断对应value的精确类型
问题背景
我们需要实现一组带value属性的对象数组,要求每个对象的value类型与其name键对应的数据模型类型严格匹配。当前实现能在输入阶段限制value的合法类型(例如不能将空字符串赋值给purposes的value),但返回字段数组时,每个元素的value类型被合并为所有可能类型的联合(如string | string[]),无法得到对应键的单一精确类型。
现有代码
type FormField<TModel> = { [K in keyof TModel]: { name: K; value: TModel[K]; }; }[keyof TModel]; type FormOptions<TModel> = { fields: FormField<TModel>[]; }; export const form = <TModel>(options: FormOptions<TModel>) => { return options.fields; // 为简化仅返回fields };
问题复现
type FormModel = { fullName: string; email: string; purposes: string[]; justification: string; }; const [fullName, email, purposes, justification] = form<FormModel>({ fields: [ { name: 'fullName', value: ''}, { name: 'email', value: ''}, { name: 'purposes', value: []}, { name: 'justification', value: ''}, ], }); const fn = fullName.value // 期望类型为string,但实际为string | string[]
问题原因
FormField<TModel>本质是TModel所有键对应字段类型的联合类型,而FormOptions<TModel>中的fields被定义为该联合类型的普通数组。TypeScript会将数组元素统一推断为联合类型,因此每个元素的value自然是所有模型值类型的联合,无法保留单个元素的精确类型信息。
解决方案
修改泛型定义,让form函数能够推断出fields的异构元组类型,而非普通数组类型,从而保留每个元素的精确类型。
方案一:基于原FormField定义修改
type FormField<TModel> = { [K in keyof TModel]: { name: K; value: TModel[K]; }; }[keyof TModel]; // 新增元组泛型Fields,约束每个元素符合FormField<TModel> export const form = <TModel, Fields extends readonly FormField<TModel>[]>(options: { fields: Fields }) => { return options.fields; };
方案二:简化泛型约束
无需单独定义FormOptions,直接在泛型中约束字段结构:
export const form = <TModel, Fields extends readonly { [K in keyof TModel]: { name: K; value: TModel[K] } }[keyof TModel][]>(options: { fields: Fields }) => { return options.fields; };
修改后效果
使用示例中解构得到的每个变量,其value类型会被正确推断为对应键的单一类型:
fullName.value→stringpurposes.value→string[]
既保留了输入时的类型校验能力,又能在返回时得到精确的类型推断。
内容的提问来源于stack exchange,提问作者Michael Motúz
相关产品推荐
相关产品推荐

