You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.09 08:25:23