Understanding TypeScript utility types
TypeScript utility types simplify type manipulation in your code. These tools are part of TypeScript's broader ecosystem of features that help you write safer, more maintainable code. They help you quickly adjust type properties—for example, making them optional, required, or read-only—and even allow you to extract or modify types for functions, constructors, and strings. By using these utilities, you can write more concise, flexible, and maintainable code without reinventing the wheel each time you need a slightly different type variation. Here's a list of all the common utility types used in TypeScript:
Utility Type | Description |
---|---|
Partial<T> | Constructs a type with all properties of T set to optional. |
Required<T> | Constructs a type with all properties of T set to required. |
Readonly<T> | Constructs a type with all properties of T marked as readonly. |
Record<K, T> | Constructs an object type whose keys are K and values are T. |
Pick<T, K> | Constructs a type by picking a set of properties K from T. |
Omit<T, K> | Constructs a type by omitting a set of properties K from T. |
Exclude<T, U> | Constructs a type by excluding from T all union members that are assignable to U. |
Extract<T, U> | Constructs a type by extracting from T all union members that are assignable to U. |
NonNullable<T> | Constructs a type by excluding null and undefined from T. |
Parameters<T> | Constructs a tuple type from the types of the parameters of a function type T. |
ConstructorParameters<T> | Constructs a tuple type from the types of the parameters of a constructor function type T. |
ReturnType<T> | Constructs a type consisting of the return type of a function type T. |
InstanceType<T> | Constructs a type consisting of the instance type of a constructor function type T. |
ThisParameterType<T> | Extracts the type of the this parameter for a function type T, if present. |
OmitThisParameter<T> | Constructs a function type with the this parameter removed from T. |
ThisType<T> | A marker type that can be used to specify the type of this in an object literal. |
Awaited<T> | Constructs a type by resolving the awaited type of a Promise-like type T. |
Uppercase<S> | Converts a string literal type S to uppercase. |
Lowercase<S> | Converts a string literal type S to lowercase. |
Capitalize<S> | Converts the first character of a string literal type S to uppercase. |
Uncapitalize<S> | Converts the first character of a string literal type S to lowercase. |
To learn more about each utility type, click the link to each respective type in the table above. In this article, we’re going to walk through a few ways these TypeScript utility types can be used to simplify your project.
Making Code Refactoring Easier with Utility Types
TypeScript utility types like Partial
, Pick
, and Omit
streamline the way you handle type modifications. Partial
makes all properties of a type optional, which is helpful when you're building objects incrementally.
interface User {
id: number;
name: string;
age: number;
}
const updateUser: Partial<User> = { name: "Alice" };
// Only 'name' is required here, thanks to Partial.
Pick
lets you create a new type by choosing specific properties from an existing type, keeping your types simple and focused.
interface User {
id: number;
name: string;
email: string;
}
type UserPreview = Pick<User, "id" | "name">;
// UserPreview now only has 'id' and 'name'.
Omit
creates a new type by removing certain properties from an existing type, which is useful for eliminating unnecessary fields.
interface User {
id: number;
name: string;
password: string;
}
type SafeUser = Omit<User, "password">;
// SafeUser has all properties of User except 'password'.
Using Utility Types for Better Type Safety
Utility types like Readonly
and Required
can enforce stricter rules. Readonly
prevents properties from being changed after initialization.
interface User {
id: number;
name: string;
}
const readonlyUser: Readonly<User> = { id: 1, name: "Charlie" };
// readonlyUser.name = "Dave"; // Error: Cannot assign to 'name' because it is a read-only property.
Required
ensures all properties have values.
interface User {
id?: number;
name?: string;
}
const completeUser: Required<User> = { id: 1, name: "Bob" };
// Now both 'id' and 'name' must be provided.
Promoting Code Reuse with Utility Types
Record
maps keys to value types, creating type-safe key-value structures.
type Role = "admin" | "user";
const userPermissions: Record<Role, string[]> = {
admin: ["create", "delete"],
user: ["read"]
};
Handling Complex Object Types with Utility Types
Extract
and Exclude
Exclude
help manage union types effectively. Extract
creates a new type by pulling matching types from a union.
type AllColors = "red" | "green" | "blue" | "yellow";
type PrimaryColors = Extract<AllColors, "red" | "green">;
// PrimaryColors is now "red" | "green".
Exclude
removes specific types from a union.
type AllColors = "red" | "green" | "blue" | "yellow";
type PrimaryColors = Exclude<AllColors, "yellow">;
// PrimaryColors is now "red" | "green" | "blue".
Making code more readable with TS utility types
NonNullable
creates a type that excludes null
and undefined
.
type MaybeString = string | null | undefined;
type DefinedString = NonNullable<MaybeString>;
// DefinedString is now just 'string'.
Working with Conditional Types Using Utility Types
Conditional types let you create types based on a condition, similar to how ternary operators work.
type IsString<T> = T extends string ? true : false;
type Test1 = IsString<"hello">; // true
type Test2 = IsString<123>; // false
Using Mapped Types with Utility Types
Mapped types iterate over an existing type to create a new one.
type MappedType<T> = {
[P in keyof T]: T[P];
};
interface OriginalType {
a: string;
b: number;
}
type MappedTypeResult = MappedType<OriginalType>;
Final Thoughts on TypeScript Utility Types
TypeScript utility types let you write cleaner, safer code with less effort. Used well, they cut down on repetitive type definitions and help catch errors early. Start with simple types like Partial
and Pick
, then explore more advanced options as your needs grow.