如何为从深度嵌套对象数组生成的对象定义泛型类型?
如何为从深度嵌套对象数组生成的对象定义泛型类型?
嘿,我明白你的需求了——你有一个深度嵌套的Form结构,想要把它转换成一个扁平的键值对对象(键是字段ID,值是对应字段的值),同时希望TypeScript能精准推断出每个字段值的具体类型,而不是统一用FieldValue的联合类型对吧?咱们一步步来解决这个问题。
首先先回顾一下你给出的原始类型和数据:
type FieldValue = string | number | Date; type Form = { fieldSets: Array<{ rows: Array<{ fields: Array<{ id: string; value: FieldValue; component: number }>; }>; }>; }; const form: Form = { fieldSets: [ { rows: [ { fields: [ { id: 'f-1', value: 'value', component: 1, }, { id: 'f-2', value: 123, component: 2, }, ], }, ], }, { rows: [ { fields: [ { id: 'f-3', value: new Date(), component: 3, }, ], }, ], }, ], };
接下来,问题的核心是:如果直接写一个普通的转换函数,返回的类型会是Record<string, FieldValue>,这样TypeScript没法知道'f-1'对应的值是string,'f-2'是number,所以咱们需要用泛型来捕获每个字段的具体ID和值类型。
第一步:定义泛型工具类型提取扁平结构
首先咱们需要一个工具类型,用来递归遍历嵌套的Form结构,自动收集所有字段的ID和对应的value类型:
// 工具类型:从嵌套Form中提取扁平的键值对类型 type FlattenForm<T> = T extends { fieldSets: infer FieldSets; } ? FieldSets extends Array<{ rows: infer Rows; }> ? Rows extends Array<{ fields: infer Fields; }> ? Fields extends Array<{ id: infer ID; value: infer V }> ? Record<ID extends string ? ID : never, V> : never : never : never : never;
第二步:编写泛型转换函数
然后咱们写一个泛型函数,用这个工具类型来约束返回值,确保类型安全:
function flattenForm<T extends Form>(form: T): FlattenForm<T> { return form.fieldSets.reduce((acc, fieldSet) => { fieldSet.rows.forEach(row => { row.fields.forEach(field => { acc[field.id as keyof FlattenForm<T>] = field.value as FlattenForm<T>[keyof FlattenForm<T>]; }); }); return acc; }, {} as FlattenForm<T>); }
第三步:验证类型推断效果
现在用你的form数据测试一下,TypeScript已经能精准推断每个字段的类型了:
const flattened = flattenForm(form); // 此时TypeScript会正确识别: flattened['f-1']; // 类型为string flattened['f-2']; // 类型为number flattened['f-3']; // 类型为Date
进阶优化:更精确的字面量类型推断
如果想让类型信息更精准,咱们可以去掉form变量的Form类型约束,加上as const让TypeScript推断字面量类型:
// 不指定Form类型,用as const锁定字面量类型 const form = { fieldSets: [ { rows: [ { fields: [ { id: 'f-1', value: 'value', component: 1, }, { id: 'f-2', value: 123, component: 2, }, ], }, ], }, { rows: [ { fields: [ { id: 'f-3', value: new Date(), component: 3, }, ], }, ], }, ], } as const; const flattened = flattenForm(form); // 此时flattened的类型会被推断为: // { // 'f-1': "value"; // 'f-2': 123; // 'f-3': Date; // }
这样既完成了嵌套结构到扁平对象的转换,又让TypeScript能完美识别每个字段的具体类型,完全满足需求!
备注:内容来源于stack exchange,提问作者BorisAtLast
相关产品推荐
相关产品推荐

