Issue
I'd like to do CRUD stuff on some unknown items (any object with id: number
).
For simplicity, let's say that for now we're deling with messages like this:
type Message =
| { id: number; type: "success"; message: string }
| { id: number; type: "error"; message: string; status: number };
let messages: Message[] = [{ id: 1, type: "success", message: "Ok" }];
Please notice that if type === "error
, status has to be provided (can't be undeifned
, since hanlder could use .toPrecision()
or other number method).
Update function can look like this (just a basic demo):
function updateItem<T extends { id: number }>(
items: T[],
id: number,
changes: Partial<T>
): T[] {
return items.map((item) => {
if (item.id === id) {
return { ...item, ...changes };
}
return item;
});
}
Let's test it:
messages = updateItem(messages, 1, { type: "error" });
console.log(messages);
// => [{ id: 1, type: "error", message: "Ok" }]
and just like that TypeScript allowed me to create invalid message.
It has type === "error"
, but it doesn't have a status.
It would notify about if I would implement it like this:
function updateMessage(
items: Message[],
id: number,
changes: Partial<Message>
): Message[] {
return items.map((item) => {
if (item.id === id) {
return { ...item, ...changes }; // TS ERROR
}
return item;
});
}
but as I mentioned it will be used used for generic items.
Questions
- Why TS allows
Partial
ussage in generic, but not when a type is hard coded? - How I could implement it? Idealy it should be able to detect which properties
are always optional, and which are required if some property is equal to something (in this example if we pass
type: 'error'
it would be reasonable to also requirestatus
since it may or may not be present in object). My current idea is to doPartial<DeepIntersection<T>>
, but I got stuck atDeepIntersection
part (TypeScript deep intersection of objects union)
EDIT - example of using Combine
Thanks to awesome sugestion provided by @jcalz here TypeScript deep intersection of objects union I was able to make version with hard coded types work:
function updateMessage(
items: Message[],
id: number,
changes: Partial<Omit<Combine<Message>, "id">>
): Message[] {
return items.map((item) => {
if (item.id === id) {
return { ...item, ...changes }; // No error