Farzad Yousefzadeh
  • About
  • CV
  • Appearances
  • Blog
  • Mentorship

Pick a member of a union type in Typescript

February 12, 2022
Check out all posts

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:

parameter editEvent is of type object where its key is Edit_Item, its id is a string and its updates is partial of any

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: "" }
]
*/

The materials of this website are licensed under The Creative Commons