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

如何实现类型安全的Lodash _.update工具?

类型安全的对象路径更新方案探索

Lodash 提供了一个名为update的对象工具,功能直观:

const obj = {}

_.update(obj, 'a.b.c', prev => prev ? 1 : 0)

console.log(obj) // { a: { b: { c: 0 } } }

如示例所示,第二个参数传入路径字符串,update函数会递归创建该路径,并将路径最后一项的值设置为更新器返回的结果。

但它的类型支持存在明显缺陷:

const obj = { a: { b: { c: "hello world" } } };

_.update(obj, "a.b.c", (prev) => (prev ? 1 : 0));

console.log(obj); // { a: { b: { c: 0 } } }

可以看到prev的类型是any,完全没有类型安全性。因此我尝试自行实现一个替代/包装Lodash update的方案,研究路径类型处理时找到过相关实现,但难以理解其中逻辑,更不知道如何处理计算键。

于是我围绕_.update写了如下包装函数:

export function mutate<T extends object, P extends object = object>(
  obj: T,
  path: P,
  updater: (prev: ValueOf<T>) => ValueOf<T>
): T {
  const actualPath = Object.keys(path)
    .map((o) => path[o as keyof P])
    .join(".");

  return _.update(obj, actualPath, updater);
}

const obj = { a: { b: { c: 123 } } };

const x = "a";
const y = "b";
const z = "c";

mutate(obj, { x, y, z }, (prev) => 123);

目前已经有一定进展,mutate会提示123与{ b: { c: number } }类型不兼容,但该函数仍缺乏递归处理能力,不清楚后续如何推进。

技术问题解答

  1. 是否可以获取计算属性的类型?
    可以。TypeScript支持通过模板字面量类型和递归类型提取计算键的类型信息,只要计算键的类型是字面量类型或可推导的字符串类型,就能被捕获。

  2. 具体该如何实现?
    核心是通过递归类型解析对象路径并约束参数:

  • 定义Path类型,提取对象的所有合法嵌套路径;
  • 定义PathValue类型,根据路径获取对应的值类型;
  • 让mutate函数的path参数约束为目标对象的合法路径,更新器的prev参数自动推导为路径对应的原始值类型。

示例核心类型与函数实现:

type Path<T> = T extends object
  ? { [K in keyof T]: K extends string ? `${K}` | `${K}.${Path<T[K]>}` : never }[keyof T]
  : never;

type PathValue<T, P extends Path<T>> = P extends `${infer K}.${infer Rest}`
  ? K extends keyof T
    ? Rest extends Path<T[K]>
      ? PathValue<T[K], Rest>
      : never
    : never
  : P extends keyof T
  ? T[P]
  : never;

// 改进后的类型安全mutate函数
export function mutate<T extends object, P extends Path<T>>(
  obj: T,
  path: P,
  updater: (prev: PathValue<T, P> | undefined) => PathValue<T, P>
): T {
  return _.update(obj, path, updater);
}

对于计算键,只要变量是字面量类型(比如const x = "a"的类型是"a"而非string),上述类型就能自动识别并约束路径合法性。

  1. 如何递归处理该问题?
    通过递归类型+工具函数结合实现:
  • 类型层面:Path递归遍历对象键,拼接所有嵌套路径字符串;PathValue递归解析路径,逐层获取末端值类型;
  • 函数层面:依赖Lodash update本身的递归路径创建能力,类型系统确保每一层路径的合法性。
  1. 相关库推荐
  • Immer:专注不可变对象更新,支持类型安全的嵌套修改,通过produce函数实现,语法直观;
  • ts-toolbelt:提供大量工具类型,包含路径解析、值提取能力,可用于构建类型安全的对象操作函数;
  • remeda:替代Lodash的现代工具库,原生支持TypeScript,内置类型安全的对象更新方法;
  • Zod:虽主打Schema验证,但其类型推导能力可辅助实现类型安全的对象路径操作。

内容的提问来源于stack exchange,提问作者Guilherme Oderdenge

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 10:55:14