DEV Community

Cover image for TypeScript utility types I use the most
Danijel Maksimovic
Danijel Maksimovic

Posted on • Originally published at danijelmaksimovic.com

TypeScript utility types I use the most

TypeScript comes with a lot of built-in types that can be used for extracting, converting, or manipulating types in many ways. We are going to cover the ones I use the most and we will go through them in a Q&A fashion.

How do I pick a couple of properties of a type?

Let's say we have a type called Post that looks like this:

type Post = {
  title: string
  content: string
  tags: string[]
  slug: string
}
Enter fullscreen mode Exit fullscreen mode

and we want to create a new post type that only has a title and content. We could create it from scratch and duplicate Post's properties, or we can pick the properties we want from the already created type. We don't want to repeat ourselves so we can do it like so:

type DraftPost = Pick<Post, 'title' | 'description'>
Enter fullscreen mode Exit fullscreen mode

Pick utility types creates a new type from a given type by using only properties provided as a union.

How can I remove a property from a type?

In our previous example, we have chosen the properties we want to have in our new type. Let's now remove properties we don't want.

type DraftPost = Omit<Post, 'slug' | 'author'>
Enter fullscreen mode Exit fullscreen mode

Pick and Omit are two sides of the same coin, they do opposite things that can achieve the same result.

How can I pick a value from an enum or a union type?

Let's say we have an enum of post categories:

enum PostCategories {
  React
  Agile
  Fastify
  GraphQL
  Postgres
}
Enter fullscreen mode Exit fullscreen mode

and we would like to create a union of post categories that are only back-end1 related. We could try using Pick type that we saw earlier but it won't help much as Pick type works with object types. Instead, we could use Extract utility type:

type BackendCategories = Extract<PostCategories, 'Fastify' | 'GraphQL' | 'Postgres'>
Enter fullscreen mode Exit fullscreen mode

Newly-created BackendCategories type would be a union PostCategory.Fastify | PostCategory.GraphQL | PostCategory.Postgres. Extract doesn't create a new union, it transforms it into a union.

How can I remove a property from a union or an enum?

In the same fashion as Extract, there is a Extract utility type that does a similar thing as the Omit type, but for union or enum types.

Let's say we want to remove Agile from post categories:

type TechCategories = Exclude<PostCategories, 'Agile'>
Enter fullscreen mode Exit fullscreen mode

Newly-created TechCategories type would be a union of all enum values except Agile.

How do I make a type non-nullable?

Let's say we have a type that is a union of an object type and null, which is a common scenario when typing an API response:

type PostResponse = { data: Post | null }
Enter fullscreen mode Exit fullscreen mode

and let's say we have a function that is transforming API response in some manner:

function transformResponse(apiResponse: PostResponse) {
  // implementation here
}
Enter fullscreen mode Exit fullscreen mode

When trying to access the apiResponse.data properties TypeScript will error out as we are trying to access a property on a potentially nullable value. If we don't want to have the null check in our transform function we could utilize the NonNullable utility type:

function transformResponse(apiResponse: NonNullable<PostResponse>) {
  // implementation here
}
Enter fullscreen mode Exit fullscreen mode

This will make sure apiResponse.data has a Post type as NonNullable utility will remove null and undefined from the union.

How can I make all properties of a type optional?

Sometimes we want to use the existing type but only provide a subset of its properties. For example, we might have a factory function that generates objects of a certain type but would like to provide a way for a user to override generated values:

function buildPost(overrides?: Partial<Post>): Post {
  return {
    id: randomUUID(),
    title: 'Just some title',
    content: 'Description of a post',
    tags: ['tag1'],
    slug: 'just-some-title',
    ...overrides
  }
}

buildPost({ title: 'My custom title' })
Enter fullscreen mode Exit fullscreen mode

In our builder function, we are setting the overrides type to Partial of a post type, which makes all properties of a Post type optional, which gives us an option to only pass a subset of Post properties.

How can I make all properties of a type required?

Let's say we have a form to submit a new post, and we have a type to define the form's input:

type PostInput = {
  title?: string
  description?: string
  tags?: string[]
}
Enter fullscreen mode Exit fullscreen mode

but we need a type when all fields are required, like in the submit handler function:

function submitPost(postInput: Required<PostInput>) {
  // submit a post
}
Enter fullscreen mode Exit fullscreen mode

Required utility type is quite useful when we don't want to repeat ourselves just to make all properties of a type required.

How can I extract a return type of a function?

This one is quite useful when you want to get a type of what a function is returning but you don't have access to the type itself, e.g. when a third-party library exposes a typed function but it doesn't export the returned type. For example:

// third-party library
type Slug = // complex slug type that isn't exported

export function slugifyString(str: string): Slug

// your app
type Slug = ReturnType<typeof slugifyString>

type Post = {
  id: string
  title: string
  slug: Slug
}
Enter fullscreen mode Exit fullscreen mode

In our example, we are using a function that returns some complicated type (Slug) but it isn't exporting it for our use. We want to reuse that value in our application but since it's not exported we should get it in some other way. ReturnType utility type extracts the type a function is returning so we can use it in our own application.

How can get a type wrapped in a Promise?

Let's say our previous example returns a Promise instead of an object type:

// third-party library
type Slug = // complex slug type that isn't exported

export function slugifyString(str: string): Promise<Slug>

type Slug = ReturnType<typeof slugifyString> // it's a Promise now

type Post = {
  id: string
  title: string
  slug: Slug // won't work anymore
}
Enter fullscreen mode Exit fullscreen mode

Our type extraction would now return a Promise type as that's what the function is returning. We need to get to the type wrapped in a promise and we can do it like so:

type Slug = Awaited<ReturnType<typeof slugifyString>>
Enter fullscreen mode Exit fullscreen mode

This might look like a bit too much generics nesting and if it is too much for you, you can split it like so:

type SlugifyStringPromise = ReturnType<typeof slugifyString>
type Slug = Awaited<SlugifyStringPromise>
Enter fullscreen mode Exit fullscreen mode

How can I type an object literal?

Object literals are one of the most used data structures in JavaScript and rightfully so. But how do we type them?

const postsByCategories = {
  [PostCategory.React]: [post1, post2],
  [PostCategory.GraphQL]: [post3, post4]
  // ...
}
Enter fullscreen mode Exit fullscreen mode

Let's say we would like to allow only post categories as keys and only an array of posts as values:

const postsByCategories: Record<PostCategory, Post[]> = {
  [PostCategory.React]: [post1, post2],
  [PostCategory.GraphQL]: [post3, post4]
  // ...
}
Enter fullscreen mode Exit fullscreen mode

The only keys TypeScript would allow are values from PostCategory enum and only an array of Posts can be set as a value. The Record utility type is really useful when we have a map of set keys and values.


These are the utility types I use the most, although this isn't the complete list of available utility types. Had over to the TypeScript documentation to get familiar with even more utility types.

Thanks for reading!

Top comments (4)

Collapse
 
brense profile image
Rense Bakker

Very good article! You pretty much covered all the important ones! πŸ‘πŸ™‚

Collapse
 
maksimovicdanijel profile image
Danijel Maksimovic

Thanks!

Collapse
 
full_stack_adi profile image
Aditya

Nice one!

Collapse
 
maksimovicdanijel profile image
Danijel Maksimovic

Thanks!