My interest in this matter stems from curiosity. The title may be a bit complex, so let's simplify it with an example:
type ObjType = { items: Array<{ id: number }>; sth: number };
const obj: ObjType = {
sth: 3,
items: [{ id: 1 }, { id: 2 }, { id: 3 }]
};
Suppose I want to delete an item by its id from obj.items
, for instance the id 2, and return the updated object without mutating the original one. Achieving this in JavaScript is straightforward, but I aim to maintain type safety using TypeScript as well. Here is what I came up with:
function removeItemFromArray<S, A extends { id: number }>(
obj: S,
field: keyof S,
array: A[],
valueToCompare: number
): S {
return {
...obj,
[field]: array.filter(i => i.id !== valueToCompare)
};
}
The presence of the array
parameter might seem redundant since it will always refer to obj[field]
. However, if we were to simply use obj[field]
, there would be no guarantee that it actually represents an array. So, how can we ensure that obj[field]
indeed contains an array of objects with an id property? My intuition tells me that conditional types hold the key to solving this puzzle, though I'm still working on unraveling the specifics.