如何实现类型安全的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 } }类型不兼容,但该函数仍缺乏递归处理能力,不清楚后续如何推进。
技术问题解答
是否可以获取计算属性的类型?
可以。TypeScript支持通过模板字面量类型和递归类型提取计算键的类型信息,只要计算键的类型是字面量类型或可推导的字符串类型,就能被捕获。具体该如何实现?
核心是通过递归类型解析对象路径并约束参数:
- 定义
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),上述类型就能自动识别并约束路径合法性。
- 如何递归处理该问题?
通过递归类型+工具函数结合实现:
- 类型层面:
Path递归遍历对象键,拼接所有嵌套路径字符串;PathValue递归解析路径,逐层获取末端值类型; - 函数层面:依赖Lodash
update本身的递归路径创建能力,类型系统确保每一层路径的合法性。
- 相关库推荐
- Immer:专注不可变对象更新,支持类型安全的嵌套修改,通过
produce函数实现,语法直观; - ts-toolbelt:提供大量工具类型,包含路径解析、值提取能力,可用于构建类型安全的对象操作函数;
- remeda:替代Lodash的现代工具库,原生支持TypeScript,内置类型安全的对象更新方法;
- Zod:虽主打Schema验证,但其类型推导能力可辅助实现类型安全的对象路径操作。
内容的提问来源于stack exchange,提问作者Guilherme Oderdenge
相关产品推荐
相关产品推荐

