如何合并同键不同属性的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
相关产品推荐
相关产品推荐

