Utility types look complicated, but they are actually simple and super handy. Let's say you have a type and you want to customize it for a use case. Instead of duplicating that type and modifying it to fit that use case, you can use a utility type.
Here is a recap of some utility types. You can play with the examples on the TypeScript Playground.
Partial
This utility type makes all properties of a type optional. This is useful if you need to update or extend a type, but don't need to provide every property. However, TypeScript still enforces correct types for each property and does not allow arbitrary properties-all properties must be included in the type definition.
interface User {
name: string;
age: number;
}
const user1: Partial<User> = { name: "Audrey" }; // age is optional
const user2: Partial<User> = { age: 33 }; // name is optional
const user4: Partial<User> = { name: 33 }; // Error: Type 'number' is not assignable to type 'string'
const user5: Partial<User> = { whatever: 33 }; // Error: Object literal may only specify known properties
const user6: Partial<User> = {}; // can be empty since it makes all properties optional
Required
The opposite of Partial, it makes all properties of a type mandatory. This can be useful when you want to make sure that a type has all of its properties set, even if it was previously optional.
interface Shape {
color?: string;
width?: number;
}
const shape1: Required<Shape> = { color: "green", width: 40 }; // both properties are mandatory
const shape2: Required<Shape> = {color: "blue"} //Error: Property 'width' is missing in type '{ color: string; }' but required in type 'Required<Shape>'
Readonly
This makes all properties of a type read-only, meaning they can't be reassigned after creation.
interface User {
name: string;
age: number;
}
const user: Readonly<User> = { name: "Alice", age: 25 };
user.age = 26; // Error: Cannot assign to 'age' because it is a read-only property
Record
This creates an object type with keys of type K and values of type T. It’s useful when you want to enforce a shape for an object with dynamic keys.
type Role = "admin" | "user" | "guest";
const userRoles: Record<Role, string> = {
admin: "Administrator",
user: "Regular User",
guest: "Guest User",
};
Pick
This type constructs a new type by picking a set of properties K from type T. It’s useful when you need a subset of properties from an existing type.
In this example, pickedUser
object is correctly typed because it includes only the name property. It is then assigned to an object, which matches the expected type.
interface User {
name: string;
age: number;
}
const pickedUser: Pick<User, "name"> = { name: "Audrey" };
const pickedUser2: Pick<User, "name"> = { name: "Audrey", age: 33 }; // Error: 'age' does not exist in type 'Pick<User, "name">'
What do utility types compile to?
Because TypeScript is a superset of JavaScript, utility types exist only at compile time to enforce type safety and do not affect runtime output. The final JavaScript output is just plain JavaScript objects with no TypeScript type constraints.
The examples above compile to:
"use strict";
//Partial
const user1 = { name: "Audrey" };
const user2 = { age: 33 };
const user6 = {};
//Required
const shape1 = { color: "green", width: 40 };
//Readonly
const user = { name: "Alice", age: 25 };
//Record
const userRoles = {
admin: "Administrator",
user: "Regular User",
guest: "Guest User",
};
//Pick
const pickedUser = { name: "Audrey" };
Make your own utility types
TypeScript enables the creation of custom utility types using generics and mapped types. For instance, you can define your own version of Partial.
The keyof T
type in TypeScript represents all the property names (keys) of T. In the example, [K in keyof T]
iterates over each key, while T[K]
ensures the property retains its original type, and ?
makes each property optional.
The custom MyPartial<T>
type behaves similarly to Partial<T>
, making all properties optional.
type MyPartial<T> = {
[K in keyof T]?: T[K];
};
interface User {
name: string;
age: number;
}
const user: MyPartial<User> = { name: "Alice" }; // 'age' is optional
I hope this was helpful! You can find all the available utility types in the TypeScript documentation.
Feel free to reach out if you have any questions! You can also find me on Github, BlueSky, LinkedIn, and Instagram.
Top comments (3)
Excellent article!! I was looking exactly for this.
thank you @juan_labrada_42e0d23118f4 :)
Really good one.