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

在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 19:01:03