DEV Community

Cover image for Best ways to use "Extract" utility type in Typescript
Arafat
Arafat

Posted on • Updated on

Best ways to use "Extract" utility type in Typescript

Extract is a utitly type in Typescript that allows you to create a new type by extracting a subset of an existing type.

This article is inspired from this article from Matt Pocock.

Here are few possible ways to use Extract in Typescript:

Table of contents

Get a member of an union

We can extract a member of union by using Extract. The first argument is the full union, and the second argument is the member of the union that we want to get.

type Animal = "cat" | "dog" | "bird" | "fish";

type OnlyCat = Extract<Animal, "cat">; // 'cat'
Enter fullscreen mode Exit fullscreen mode

Get multiple members from an union

We can even get mulitple members from an union by using Extract. Here is how:

type Animal = "cat" | "dog" | "bird" | "fish";

type ExtractType = Extract<Animal, "cat" | "dog">; // 'cat' | 'dog
Enter fullscreen mode Exit fullscreen mode

Even if we pass a value to Extract that doesn't exist in the union, Typescript will return the values that exists in the union.

type Animal = "cat" | "dog" | "bird" | "fish";

type ExtractType = Extract<Animal, "cat" | "dog" | "tiger">; // 'cat' | 'dog'
Enter fullscreen mode Exit fullscreen mode

Get a member of a discriminated union

type Shape =
  | {
      type: 'square'
    }
  | {
      type: 'circle'
    }
  | {
      type: 'triangle'
    }
Enter fullscreen mode Exit fullscreen mode

A discriminated union type is one where each member has a discriminant property. The discriminant property is a common property that can be used to discriminate between the other members. In the example above, the type property is used to discriminate between the different shapes.

We can extract a member of the discriminated union by using Extract like this:

type SqaureShape = Extract<Shape, {type: 'square'}> // { type: 'square' }
Enter fullscreen mode Exit fullscreen mode

Get multiple members of a discriminated union

We can also extract multiple members of a discriminated union:

type SqaureAndCircleShape = Extract<Shape, {type: 'square'} | {type: 'circle'}> 
// { type: 'square' } | { type: 'circle' }
Enter fullscreen mode Exit fullscreen mode

This works even if the union members have other properties attached to them.

type Shape =
  | {
      type: 'square';
      size: number;
    }
  | {
      type: 'circle'
    }
  | {
      type: 'triangle'
    }

type SqaureAndCircleShape = Extract<Shape, {type: 'square'} | {type: 'circle'}> 
// {type: 'square'; size: number;} | { type: 'circle' }
Enter fullscreen mode Exit fullscreen mode

You can also get multiple members of the discriminated union by passing a union to the type property:

type Shape =
  | {
      type: 'square';
      size: number;
    }
  | {
      type: 'circle'
    }
  | {
      type: 'triangle'
    }

type SqaureAndCircleShape = Extract<Shape, {type: 'square' | 'circle'}> 
// {type: 'square'; size: number;} | { type: 'circle' }
Enter fullscreen mode Exit fullscreen mode

Get members of a discriminated union by shape

type Routes =
  | {
      route: '/user'
      search: {
        id: string
      }
    }
  | {
      route: '/user/create'
    }
  | {
      route: '/user/edit'
      search: {
        id: string
      }
    }

type RoutesWithSearch = Extract<
  Routes,
  {
    search: any
  }
>
/* 
{
    route: '/user';
    search: {
        id: string;
    };
} | {
    route: '/user/edit';
    search: {
        id: string;
    };
}
*/
Enter fullscreen mode Exit fullscreen mode

To get the members of union you don't need to always include the discriminator (in this case, route) in the second argument. You can just pass the shape of the members you want to get.

In this case, we want to extract the types from the Routes union type that have a search property.


Get members by a type

type allTypes = 'admin' | 'user' | 5 | 6 | 7 | true

type onlyNumbers = Extract<allTypes, number> // 5 | 6 | 7
Enter fullscreen mode Exit fullscreen mode

In the example above, we remove all literals that don't match the number type from the allTypes union. Hence, we are only getting the numbers.


Get strings containing a substring from an union

type keys = 'userId' | 'tweetId' | 'userName' | 'tweetName'

type UserKey = Extract<keys, `${'user'}${string}`> // "userId" | "userName"
Enter fullscreen mode Exit fullscreen mode

The resulting UserKey type will be a union of the string literals from strings that start with the string "user." This case will include only the userId and userName string literals.


Get strings with one of several possible values from an union

type keys = 'userId' | 'tweetId' | 'id' | 'userName' | 'friendName'

type OnlyIdKey = Extract<keys, `${string}${'id' | 'Id'}${string}`> 
// "userId" | "tweetId" | "id"
Enter fullscreen mode Exit fullscreen mode

You can also use Extract to get all strings from a union that contain one of several possible substrings.

In this case, the resulting OnlyIdKey type will be a union of the string literals from keys containing the id or Id. This case will include the userId, tweetId, and id string literals.



Conclusion

So that's it, guys. I hope you found the article helpful. Extract is a utility type that can be used in many ways. If you think there are more ways to use Extract, please let me know in the comments. Thanks for reading the article. I will see you all in my next article🐸.

Visit:
👨‍💻My Portfolio
🏞️My Fiverr

Top comments (8)

Collapse
 
brense profile image
Rense Bakker

How does Extract differ from Pick?

Collapse
 
arafat4693 profile image
Arafat

Extract creates a new type by selecting some of the original types, while Pick is used to create a new type by choosing some of the original properties.

Here's an example to illustrate the difference:

interface Person {
  name: string;
  age: number;
  email: string;
  phone: string;
}

// Extract a subset of types from Person
type PersonKeys = Extract<keyof Person, 'name' | 'email'>;
// Result: type PersonKeys = "name" | "email"

// Pick some properties from Person
type PersonInfo = Pick<Person, 'name' | 'age'>;
// Result: type PersonInfo = { name: string; age: number; }
Enter fullscreen mode Exit fullscreen mode
Collapse
 
uttam_py profile image
Uttam Sharma

I had same quesiton :)

Collapse
 
tqbit profile image
tq-bit

I really liked the bit about 'Get strings containing a substring from an union'. Tbf I didn't know you could use string literals that way for TS types, especially not inside of generics.

Collapse
 
arafat4693 profile image
Arafat

Thanks, glad you learned a new thing today from the article😊

Collapse
 
gixxerblade profile image
Steve Clark 🤷‍♀️

Great post!

Collapse
 
yw662 profile image
yw662 • Edited

I am not sure but does Extract<a, b> mean a & b ?

Collapse
 
rip21 profile image
Info Comment hidden by post author - thread only accessible via permalink
Andrii Los

Looks like somebody likes to steal work of others?
Stop doing that, it's pathetic and unethical.

This article is a rewrite of this one posted a week ago:
totaltypescript.com/uses-for-exclu...

Some comments have been hidden by the post's author - find out more