在TypeScript中处理任意JavaScript数据对象的最优方案
TypeScript动态嵌套对象合并方案问题
我正在开发一个TypeScript状态维护工具,需要接收JSON格式的初始数据对象和后续更新信息,将两者合并——既支持更新已有属性,也能添加新属性,但无法提前知晓对象的嵌套结构。
场景示例
初始对象:
{ "kitchen": { "door": "closed", "cooker": "off", "objects": [ "saucepan", "fork", "chopping board", "fresh coriander"], "people": {} } }
三次更新依次是:
// 更新1 { "kitchen": { "door": "open" } } // 更新2 { "kitchen" : { "people": { "basil": { "wearing": ["hat", "glasses"], "carrying": [] } } } } // 更新3 { "kitchen" : { "people": { "basil": { "carrying": [ "fork" ] } }, "objects": [ "saucepan", "chopping board", "fresh coriander"] } }
最终期望合并结果:
{ "kitchen": { "door": "open", "cooker": "off", "people": { "basil": { "wearing": ["hat", "glasses"], "carrying": [ "fork" ] } }, "objects": [ "saucepan", "chopping board", "fresh coriander"] } }
这些都是纯JSON数据对象,JavaScript里很容易遍历结构做合并,但TypeScript依赖预定义接口,而我这个模块没法提前定义结构。核心需求就是动态新增/更新任意嵌套层级的属性,想知道最优实现方式——要么自己写嵌套字典对比合并逻辑,要么找合适的NPM模块。
方案一:手写递归合并函数
TypeScript可以用Record<string, unknown>代替any,保留基础类型安全的同时实现动态结构处理,结合递归完成深度合并:
function deepMerge<T extends Record<string, unknown>>(target: T, source: Record<string, unknown>): T { const output = { ...target }; for (const key in source) { if (source[key] instanceof Object && !(source[key] instanceof Array)) { // 嵌套对象递归合并 if (!(key in output)) { Object.assign(output, { [key]: source[key] }); } else { output[key as keyof T] = deepMerge( output[key as keyof T] as Record<string, unknown>, source[key] as Record<string, unknown> ) as T[keyof T]; } } else { // 数组、基础类型直接覆盖(匹配示例需求) output[key as keyof T] = source[key] as T[keyof T]; } } return output; } // 使用示例 const initialState = { /* 初始对象 */ }; let currentState = deepMerge(initialState, update1); currentState = deepMerge(currentState, update2); currentState = deepMerge(currentState, update3);
关键说明
- 用
Record<string, unknown>避免any的类型不安全问题 - 数组直接覆盖(和示例中更新3替换整个
objects数组的逻辑一致) - 嵌套对象递归处理,保留原对象未被更新的属性
方案二:使用成熟NPM模块
如果不想自己维护递归逻辑,直接用社区成熟工具更省心:
- lodash.merge:最常用的深度合并工具,支持嵌套对象、数组处理,TypeScript类型友好
使用方式:import merge from 'lodash.merge'; const mergedState = merge(initialState, update1, update2, update3); - deepmerge:轻量级深度合并库,可自定义数组合并策略(默认合并数组,改成替换的话传配置)
使用方式:import deepmerge from 'deepmerge'; const mergedState = deepmerge.all([initialState, update1, update2, update3], { arrayMerge: (target, source) => source });
类型提示优化
虽然无法提前定义具体结构,但可以用泛型让合并函数返回更贴合的交叉类型:
function deepMerge<T, U>(target: T, source: U): T & U { // 内部递归逻辑同上 }
这样合并后的对象会自动推断出T & U的交叉类型,保留属性的类型提示。
内容的提问来源于stack exchange,提问作者glenatron
相关产品推荐
相关产品推荐

