← 中文资源

简体中文版本

Type-Fest DeepPartial 深度合并工具

用 type-fest 的 DeepPartial 类型表达嵌套补丁,实现递归合并工具:补丁只覆盖出现的字段,结果仍是完整配置类型。

阅读英文原文

示例说明

PATCH 更新接口的难点是类型表达:顶层 Partial 只允许一层可选,嵌套对象的局部更新就要整体提供。DeepPartial 递归地把每一层都变可选,patch 参数被精确约束为任意深度的局部形态;合并函数返回完整类型,调用方拿到的配置保证所有必填字段都在——类型系统替你核对了完整性。

技术要点

  • DeepPartial 递归可选化嵌套结构
  • 数组的对象语义不参与深度合并
  • 返回类型保持完整而非部分

代码解读

mergeConfig 遍历补丁键,当补丁值与基值都是普通对象时递归合并,否则直接覆盖;展开运算符先复制基线保证原对象不被修改。示例补丁只改端口与暗色模式,数据库配置原样透传,输出类型仍是 ServerConfig。

使用建议

undefined 与缺失键在合并语义上要明确区分,当前实现按覆盖处理。深度合并遇到 Map、Set 等结构时应按业务定制。热更新配置时可结合变更监听做增量生效。

代码示例

import type { DeepPartial } from 'type-fest';

interface ServerConfig {
  http: { port: number; bodyLimitMb: number };
  db: { url: string; poolSize: number };
  features: { auditLog: boolean; darkMode: boolean };
}

function mergeConfig<T extends object>(base: T, patch: DeepPartial<T>): T {
  const result: Record<string, unknown> = { ...base };

  for (const [key, patchValue] of Object.entries(patch)) {
    const baseValue = (base as Record<string, unknown>)[key];
    const bothObjects =
      patchValue && typeof patchValue === 'object' && !Array.isArray(patchValue)
      && baseValue && typeof baseValue === 'object' && !Array.isArray(baseValue);

    result[key] = bothObjects
      ? mergeConfig(baseValue, patchValue)
      : patchValue;
  }
  return result as T;
}

const defaults: ServerConfig = {
  http: { port: 3000, bodyLimitMb: 1 },
  db: { url: 'postgres://localhost/app', poolSize: 10 },
  features: { auditLog: true, darkMode: false },
};

const merged = mergeConfig(defaults, { http: { port: 8080 }, features: { darkMode: true } });
console.log(merged.http.port, merged.db.poolSize, merged.features.darkMode);