Skip to main content

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 TypeDescription
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 Readonlyand 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.