Published on July 14, 2025
TypeScript is popular for its type safety and ability to catch bugs early, but its structural typing means every shape has to be described manually, which can become repetitive, brittle, and error-prone. When you need a slight variation of an existing type (for example, with some of the fields optional or removed) you often have to write a whole new type from scratch. This can become frustrating if you have many slightly different types and you need to do it over and over.
Fortunately, there are TypeScript utility types built in that can help to solve this problem. They let you construct new types by transforming existing ones without needing to write them all over again.
Utility types in TypeScript are built-in, globally available types that make it easy to create your own new types by transforming existing types or interfaces. You use them by wrapping an existing type around them to create a new one. Utility types use generics under the hood, but you don't need to understand generics to be able to use them.
Each utility type provides a different type of shortcut for deriving a new type from an existing one. For example, let's take a look at one of the utility types, ```Pick```, which allows you to pick a subset of fields from your existing type to copy into your new type.
Most examples here will start with an existing type:
With Pick
you can derive a subset of the User
type with just a union:
type MiniUser = Pick<User, ‘id’ | ‘name’>;
We will see what this can do for you below, but of course, for much larger and more complex types, this can save time and ensure the property types match.
This guide will get you started with some of the utility types through the use of practical examples, including API requests, web forms, and UI components. It covers the most commonly used utility types: Pick
, Partial
, Omit
, Readonly
, Record
, Required
, NonNullable
, Extract
, and Parameters
.
Both new and experienced TypeScript developers should look into utility types, because they will allow you to write more robust and flexible code with less boilerplate.
For example, a pain point in TypeScript solved with utility types is React components requiring only particular properties. Previously, you would have to check every property needing to be passed into your component, but now you can ensure the type has all the properties it needs and only those, like here, with Pick
using the User
type:
With the type specified in the parameters, any code calling this component function will see a warning in the IDE for the role
property, because it doesn't match any of the properties in the parameter’s type definition.
Utility types are defined using generics and, while we won’t go into them here, you will see the notation used throughout this post.
Let’s take a look at some of the key utility types in TypeScript, with some practical examples of how they can be applied to existing types.
Pick
is particularly useful when you want to reuse part of an existing type, but not the whole thing — for example, when a form or API only needs a few specific fields. Using Pick
allows you to ensure the types of the underlying properties remain the same, even if the original, or parent, type is updated.
Let’s say we have the User
type defined earlier and we want to create a version of it that only contains the id
and name
properties. Below is how we would do that with the Pick
utility type:
type PublicUser = Pick<User, 'id' | 'name'>;
The User
type is the base, or original type to derive from, and 'id' | 'name'
is a union of the property names that we want to extract from User
into our new type named PublicUser
.
Imagine you're registering a new user through an API, but it only needs the name and email fields at this stage. It doesn't need the entire User
object, so you would want to create a new type for that payload. Here is how Pick
helps you define that payload and how you might then use it in an Axios API request.
Using the Pick
utility type ensures that the payload
object cannot contain any other properties than name
and email
, and those properties will have the same types as they have in the original User
type definition.
This is an easy one — it’s simply the opposite of Pick
. Whereas Pick
allows you to create a type by selecting the properties you want to keep, Omit
lets you create a type by selecting the properties you don’t want to keep.
Taking the User
example again, we can create a type where only the named properties (the Keys
) are removed and display it in a React component that will only display the retained properties:
This component can then be used by populating a PublicUser
object and passing it to the component.
The Partial
utility type transforms a type so that its properties are all optional, which allows you to partially update objects of that type.
Let’s take a look at an example using our User
type again. Imagine we want to allow a user to update their name. Without the Partial
type, calling the updateUser
function below would cause an error because the User
type that the function takes expects all required fields but is only provided with one:
To solve this problem, we can make a partial instance of the User
type. Simply adding this extra type will make the above code work:
The Required
utility type is essentially the opposite of Partial
. Whereas Partial
makes all properties optional, the Required
utility type removes all optional (?
) property modifiers, making each property required.
This is extremely useful when you’re intending to write to a database and need to ensure that every property in your request is populated to avoid validation errors. An Axios API call, for example, would require this kind of certainty when receiving client requests.
This is a handy type to use when you don’t want updates performed on an object. It makes all the properties in the derived type read-only so they cannot be written to — in other words, it makes them immutable.
It's very simple to use with only one parameter: the type to derive from. Here is how you would make a read-only version of our User
type for when you want to prevent your returned object from being manipulated.
Attempting to modify the read-only object will now result in an error and it will remain unchanged.
This utility type is unique because, unlike the others we've seen so far, it doesn't modify an existing type — it simply creates a new type from scratch. It is used to create a dictionary-style type, where each key maps to a value of the same type — and that type is whatever type you're passing to it.
So, we might want to create a dictionary-style object to store a series of users from our User
type.
This object can then be used in a React component to display each user’s information.
Like so:
If null
or undefined
values would break your forms and APIs, you can use the NonNullable
utility type to ensure that these values are excluded. It converts any type used in its declaration into, as its name suggests, a non-nullable (and non-undefinable) type.
So, by using NonNullable
, you can safely create a form knowing that it will not try to render any null
or undefined
values. Here is a simplified example using our User
type:
Extract<Type, Union>
is useful when you're working with a union type and want to create a new type that is a subtype of it. For example, if you have a union type of user roles, you might want to extract any administrator-level accounts into a new type so those users can have access to sensitive information.
You can pass either a union (for example, superadmin | admin
) or a single type (superadmin
) as the second argument. Extract
will create a new type with the same shape as the original, but the type will be filtered.
The is
keyword allows TypeScript to narrow the type of role
after a runtime check. UserRole
is accepted as input, but if the check passes, the type is treated as the narrower subset ElevatedRole.
Here is a quick, non-exhaustive list of things to check for when you are using TypeScript’s utility types. Keeping these in mind will help you avoid some pitfalls of typing in this strongly typed language:
Avoid misusing Partial<T>
to stop TypeScript from complaining about missing required fields. This is easy to do when you're still prototyping, but it’s easy to forget about it once you head to production.
Don’t over-nest utility types like <Pick<Partial<T>>
. Nesting can be very useful, and maybe you do sometimes need chained behavior, but your code will be much more readable if you split your types out and name them separately.
Check you’re not using Partial<T>
for non-updates. If you allow objects to be created with a Partial
type, you’re effectively allowing any value, even {}
, to be constructed, and TypeScript won’t point the error out to you.
It’s dangerous to use Omit
to remove required fields without replacing them. For example, if you remove an id
field but don't replace it with an automatically generated id
, you will simply have no id
!
Finally, as with any good code practices, don’t overuse utility types. Keep them applicable to the situation and use them only where necessary.
TypeScript utility types are powerful because they enable you to transform and control your data types in flexible ways. They help to make your code more expressive without tedious boilerplate used everywhere.
Now that you’ve seen how some of the utility types in TypeScript can be used, you can go try them out in your own code and take a look at the others. You’ll find them quite powerful and useful.
If you like TypeScript and utility interfaces for their structure, you’ll like that your content model in Contentful can be defined to align with TypeScript types and leverage type checking when working with content from Contentful.
As well as keeping things type safe, Contentful also gives your marketing colleagues the ability to update content without having to go through developers each time. That way, you can allow non-developers to use it while keeping your code type safe.
Subscribe for updates
Build better digital experiences with Contentful updates direct to your inbox.