Read about Extract
on the official Typescript docs.
When dealing with union types in Typescript, sometime you want to pick a member of that union type based one of its properties.
Say you have a union type for all the possible events you can send to an application, let's call it AppEvent
.
type AppEvent =
| { type: "Add_Item"; item: Item }
| { type: "Edit_Item"; id: string; updates: Partial<Item> };
And later you want to write a small function that needs to do something on a particular app event. If there was a way to pass the entire event to the function and borrowing its type from the predeclared AppEvent type, it'd save you some maintaining time later on.
function doSometingAboutEdit(editEvent: unknown) {
// blah blah
}
The way to borrow the typings of "Edit_Item"
event from AppEvent
is to use the recently introduced utility called Extract
. Extract let's you query for members of a union type based on a property type.
Using Extract
, you can easily type the editEvent
parameter as:
function doSometingAboutEdit(
editEvent: Extract<AppEvent, { type: "Edit_Item" }>
) {
// blah blah
}
The great thing about the Extract
is that it will give you the typing of that entire event object. Look at the type of object in the screenshot below:
Here is a link to the Typescript playground to see the above code in action
Querying multiple members
Extract
will give you a union type of all the original union members who satisfy the test of the query.
From our example above, if we extend AppEvent
to more members, we can see our little query gives back all the members who pass the test.
type AppEvent =
| { type: "Add_Item"; item: Item }
| { type: "Edit_Item"; id: string; updates: Partial<Item> }
| { type: "Delete_Item"; id: string };
type QueryById = Extract<AppEvent, { id: string }>;
/*
| { type: "Edit_Item"; id: string; updates: Partial<Item> }
| { type: "Delete_Item"; id: string };
*/
You can think of Extract as Array.prototype.filter
but for types.
Here is a little code that might help you remember what extract does but don't take this as an accurate comparison.
const appEvent = [
{ type: "Add_Item", item: {} },
{ type: "Edit_Item", id: "", updates: {} },
{ type: "Delete_Item", id: "" },
];
const queryById = appEvent.filter((event) => event.hasOwnProperty("id"));
/*
[
{ type: "Edit_Item", id: "", updates: {} },
{ type: "Delete_Item", id: "" }
]
*/