如何在TypeScript中实现对象键的动态操作与数组结构转换?
问题
我有一组对象数组数据,希望将其动态转换为键对应数组的对象。例如,将包含多个对象的data1转换为各键对应值数组的data2。目前的简单方案在键数量增多时不够简洁,JavaScript中的动态写法在TypeScript中会出现类型错误,求更优的简洁方案。
示例数据
const data1 = [ {a: 'st1', b: 1, c: 1, d: 1, e: 'e1' }, {a: 'st2', b: 2, c: 2, d: 2, e: 'e2'}, {a: 'st3', b: 3, c: 3, d: 3, e: 'e3'}, {a: 'st4', b: 4, c: 4, d: 4, e: 'e4' }, ];
目标结构
const data2 = { a: ['st1', 'st2', 'st3', 'st4'], b: [1, 2, 3, 4], c: [1, 2, 3, 4], d: [1, 2, 3, 4], e: ['e1', 'e2', 'e3', 'e4'] };
现有方案(局限性明显)
当键数量较少时可用,但键增多后重复代码过多:
type Data2Props = { a: string[], b: number[], c: number[], d: number[], e: string[] }; const data2: Data2Props = { a: [], b: [], c: [], d: [], e: [] }; data1?.forEach((item) => { data2.a.push(item.a); data2.b.push(item.b); data2.c.push(item.c); data2.d.push(item.d); data2.e.push(item.e); });
JavaScript动态写法(TypeScript类型报错)
这种写法在JS中可行,但TS会因为类型不匹配报错:
let keys: string[] = ['a', 'b', 'c', 'd', 'e']; let data2 = {}; keys.forEach((key) => data2[key] = []); data1?.forEach((item) => { keys.forEach((key) => data2[key].push(item[key])); });
解决方案
方案1:泛型+reduce实现类型安全的通用转换
通过泛型推导原数组元素的类型,用reduce遍历构建目标对象,同时保证类型完全匹配:
// 定义类型映射:将对象的每个值类型转为对应数组类型 type ObjectToRecordOfArrays<T> = { [K in keyof T]: T[K][]; }; function transformArray<T extends Record<string, any>>(arr: T[]): ObjectToRecordOfArrays<T> { if (!arr.length) return {} as ObjectToRecordOfArrays<T>; // 获取原对象的键,确保类型为keyof T const keys = Object.keys(arr[0]) as Array<keyof T>; return arr.reduce((acc, item) => { keys.forEach(key => acc[key].push(item[key])); return acc; }, keys.reduce((init, key) => { init[key] = []; return init; }, {} as ObjectToRecordOfArrays<T>)); } // 使用示例 const data2 = transformArray(data1); // data2类型会被自动推导为:{a: string[], b: number[], c: number[], d: number[], e: string[]}
方案2:简化版直接实现
如果不需要封装成通用函数,可直接写简洁代码并处理类型:
const data2 = (data1 || []).reduce((acc, item) => { Object.entries(item).forEach(([key, value]) => { (acc[key as keyof typeof acc] as any[]).push(value); }); return acc; }, (Object.keys(data1[0] || {}) as Array<keyof typeof data1[0]>).reduce((init, key) => { init[key] = []; return init; }, {} as { [K in keyof typeof data1[0]]: (typeof data1[0][K])[] }));
方案3:借助lodash简化(项目已引入lodash时)
如果项目依赖lodash,可利用其工具函数快速实现,同时通过泛型保证类型:
import { zipObject, map, keys } from 'lodash'; type DataItem = typeof data1[0]; const data2 = zipObject(keys(data1[0]), keys(data1[0]).map(key => map(data1, item => item[key]))) as { [K in keyof DataItem]: DataItem[K][] };
内容的提问来源于stack exchange,提问作者Hadi Rasekh
相关产品推荐
相关产品推荐

