如何为TypeScript函数的扩展参数准确推断每个Field的类型?
问题
需要为类型函数的每一个扩展参数推断类型,现有字段定义:
type Field<T> = { value: T, name: string } const field1 = { value: 12, name: 'age' } const field2 = { value: 'foo', name: 'nickname' }
要将这些字段作为扩展参数传入useForm函数,调用方式如下:
useForm('registration', field1, field2)
尝试使用条件类型推断后,代码如下:
type InferredFields<T> = T extends { value: infer V }[] ? Record<string, Field<V>> : never const useForm = <T extends Field<unknown>[]>(name: string, ...args: T) => { const fields: InferredFields<T> = args.reduce( (res, field) => ({ ...res, [field.name]: field.value, }), {} as InferredFields<T>, ) return { name, fields } } const form = useForm('bar', field1, field2)
但无法根据传入的字段正确区分类型:
type FieldValue<T> = T extends { value: infer V } ? V : never // 问题:form返回类型为 { fields: Record<string, string | number> } // 而非具体的键值映射类型,导致以下代码报错 const v1: FieldValue<typeof field1> = form.fields['age'].value // error const v2: FieldValue<typeof field2> = form.fields['nickname'].value // error
请问如何为每个传入的Field参数正确映射值类型?
解决方案
核心问题在于之前的InferredFields对数组做了整体推断,得到的是联合类型而非每个字段的精确键值映射。需要通过元组遍历逐个提取每个Field的name和value类型,构建精准的键值对类型。
修改后的代码如下:
type Field<T> = { value: T, name: string } const field1 = { value: 12, name: 'age' } as const const field2 = { value: 'foo', name: 'nickname' } as const // 遍历元组T,为每个元素提取name作为键,Field<value类型>作为值 type InferredFields<T extends readonly Field<any>[]> = { [K in T[number] as K['name']]: Field<K['value']> } const useForm = <T extends readonly Field<any>[]>(name: string, ...args: T) => { const fields = args.reduce( (res, field) => ({ ...res, [field.name]: field, // 返回完整field对象,匹配Field<T>定义 }), {} as InferredFields<T>, ) return { name, fields } } const form = useForm('bar', field1, field2) type FieldValue<T> = T extends { value: infer V } ? V : never // 现在类型推断正确,无报错 const v1: FieldValue<typeof field1> = form.fields['age'].value const v2: FieldValue<typeof field2> = form.fields['nickname'].value
关键修改说明:
as const断言:给字段添加该断言,让TypeScript保留name的字面量类型(如'age'而非泛用的string),这是精确键值映射的前提。- 元组遍历类型:
InferredFields改为遍历元组的每个元素,用K['name']作为键,Field<K['value']>作为对应值,确保每个键都是具体字段名,值类型为对应Field的精确类型。 - reduce返回值修正:之前错误赋值
field.value,现在改为返回完整field对象,匹配Field<T>的结构定义。
修改后,form.fields会被推断为:
{ age: Field<number>; nickname: Field<string>; }
完美匹配每个字段的value类型,解决类型不匹配的报错问题。
内容的提问来源于stack exchange,提问作者Samuel Hulla
相关产品推荐
相关产品推荐

