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

如何合并同键不同属性的TypeScript对象并保留类型?

解决TypeScript中合并对象时保留专属方法类型的问题

假设我们有如下类型定义(以具体业务场景为例):

// 示例业务类型
type Item = {
  id: string;
  name: string;
  count: number;
  price: number;
};

// Stats类型,每个键对应不同类型的formatValue方法
type Stats<Item> = {
  count: {
    formatValue: (value: number) => string;
  };
  price: {
    formatValue: (value: number) => string;
  };
};

// Columns类型,每个键对应列配置
type Columns<Item> = {
  count: {
    label: string;
    dataKey: 'count';
  };
  price: {
    label: string;
    dataKey: 'price';
  };
};

我们需要将Stats<Item>的formatValue方法注入到Columns<Item>对应的键中,得到同时包含列配置和专属格式化方法的对象,但直接使用Object.fromEntries会导致formatValue变成泛化的联合类型,无法保留每个键的专属方法类型。

解决方案:使用映射类型+手动遍历赋值

1. 定义合并后的目标类型

通过TypeScript的映射类型,我们可以精确描述每个键的合并后类型:

type MergedColumns<Item, StatsType extends Record<string, { formatValue: any }>, ColumnsType extends Record<string, any>> = {
  [K in keyof StatsType & keyof ColumnsType]: ColumnsType[K] & {
    formatValue: StatsType[K]['formatValue'];
  };
};

这个类型会遍历StatsType和ColumnsType的共同键K,将ColumnsType[K]的属性与StatsType[K]的formatValue方法合并,且每个键的formatValue类型完全保留原有的专属定义。

2. 实现合并函数

使用for...in循环遍历对象(而非Object.fromEntries),结合类型断言确保类型推导正确:

function mergeColumnsWithFormat<Item, S extends Record<string, { formatValue: any }>, C extends Record<string, any>>(
  stats: S,
  columns: C
): MergedColumns<Item, S, C> {
  const merged = {} as MergedColumns<Item, S, C>;

  for (const key in stats) {
    if (columns.hasOwnProperty(key)) {
      merged[key] = {
        ...columns[key],
        formatValue: stats[key].formatValue
      } as MergedColumns<Item, S, C>[typeof key];
    }
  }

  return merged;
}

3. 使用示例

// 实例化stats和columns
const itemStats: Stats<Item> = {
  count: {
    formatValue: (val) => `${val} 件`
  },
  price: {
    formatValue: (val) => `¥${val.toFixed(2)}`
  }
};

const itemColumns: Columns<Item> = {
  count: {
    label: '商品数量',
    dataKey: 'count'
  },
  price: {
    label: '商品单价',
    dataKey: 'price'
  }
};

// 合并后对象
const mergedColumns = mergeColumnsWithFormat<Item, Stats<Item>, Columns<Item>>(itemStats, itemColumns);

// 验证类型:mergedColumns.count.formatValue仅接受number类型,返回string
mergedColumns.count.formatValue(100); // 正确
mergedColumns.price.formatValue(99.9); // 正确

为什么Object.fromEntries无法保留专属类型?

Object.fromEntries的类型推导是基于输入的可迭代对象,它无法跟踪每个键值对的具体类型关联,只能将所有值的类型合并为联合类型。而手动遍历+映射类型的方式,能让TypeScript明确每个键对应的专属类型,从而保留原有的类型信息。

内容的提问来源于stack exchange,提问作者Matt Huggins

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 02:10:15