Lodash has an object utility called update, and what it does is quite simple:
const obj = {}
_.update(obj, 'a.b.c', prev => prev ? 1 : 0)
console.log(obj) // { a: { b: { c: 0 } } }
As you can see, you specify a path (which is a string) to the second argument and the update
function will create the path recursively and will set the value of the last item in the path as the value returned by the updater.
However, the typing support is not good:
const obj = { a: { b: { c: "hello world" } } };
_.update(obj, "a.b.c", (prev) => (prev ? 1 : 0));
console.log(obj); // { a: { b: { c: 0 } } }
Turns out the type of prev
is any
, which means not safe at all. Then I considered creating my own solution (an alternative/wrapper to Lodash's update
utility) and started researching how to type a path and stumbled upon this answer, however, my greatest difficulty was understanding what was going on there—too much to digest, not to mention what about computed keys?
So I created this wrapper around _.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);
And I noticed some progress because mutate
is complaining that 123
is not compatible with { b: { c: number } }
, but as you can see, it still lacks recursion and I'm still totally confused about how to proceed from here.
Questions
- Is it possible to retrieve the type of a computed property, as shown above?
- If so, how?
- How to address it recursively, if at all possible?
- If you know any other library or npm package that addresses this exact problem, specifically with updating an object, I'd appreciate the shoutout.