TypeScript ships with a set of generic types called utility types that transform existing types into new ones. They're part of the standard library, so you don't install anything extra. Learning them replaces a lot of repetitive manual type definitions.
Introduction to Utility Types
Utility types are predefined generic types that perform common type transformations. They let you derive a new type from an existing one instead of writing it from scratch. Once you start reaching for them, you stop copy-pasting interface definitions with slight variations.
Partial<>
Partial<> takes a type and makes every property optional. The classic use case is update functions that accept a subset of fields.
interface User {
id: number;
name: string;
email: string;
}
function updateUser(id: number, user: Partial<User>) {
// Implementation to update user
}
updateUser now accepts any combination of User properties without requiring all of them.
Required<>
Required<> is the inverse of Partial<>. It takes a type where some or all properties are optional and marks every property as required.
interface User {
id?: number;
name?: string;
email?: string;
}
type CompleteUser = Required<User>;
CompleteUser requires all three fields, regardless of the optionality in the source interface.
Readonly<>
Readonly<> makes every property on a type non-writable after initialization. Useful for configuration objects and other data that should not be mutated.
interface User {
id: number;
name: string;
}
const user: Readonly<User> = {
id: 1,
name: 'John Doe',
};
// This will cause an error:
// user.name = 'Jane Doe';
Pick<>
Pick<> constructs a new type from a subset of an existing type's properties. Handy when you need only part of a larger interface.
interface User {
id: number;
name: string;
email: string;
isAdmin: boolean;
}
type Admin = Pick<User, 'id' | 'isAdmin'>;
const adminUser: Admin = {
id: 1,
isAdmin: true,
};
Omit<>
Omit<> is the complement of Pick<>. It produces a type that excludes specified properties. Common for stripping sensitive fields before sending data to clients.
interface User {
id: number;
name: string;
email: string;
password: string;
}
type PublicUser = Omit<User, 'password'>;
const publicUser: PublicUser = {
id: 1,
name: 'John Doe',
email: 'john@example.com',
};
Record<>
Record<> builds an object type with a fixed set of keys and a uniform value type. It's the right choice for typed lookup tables.
type Roles = 'admin' | 'user' | 'guest';
interface Permissions {
canEdit: boolean;
canDelete: boolean;
}
type RolePermissions = Record<Roles, Permissions>;
const permissions: RolePermissions = {
admin: { canEdit: true, canDelete: true },
user: { canEdit: true, canDelete: false },
guest: { canEdit: false, canDelete: false },
};
Exclude<>
Exclude<> filters a union type by removing members that match a given type.
type Status = 'success' | 'error' | 'pending';
type NonErrorStatus = Exclude<Status, 'error'>;
// NonErrorStatus is 'success' | 'pending'
Extract<>
Extract<> is the reverse: it keeps only the union members that match a given type.
type Status = 'success' | 'error' | 'pending';
type ErrorStatus = Extract<Status, 'error' | 'pending'>;
// ErrorStatus is 'error' | 'pending'
NonNullable<>
NonNullable<> strips null and undefined from a type. Useful at the boundary between nullable data and code that expects a concrete value.
type Name = string | null | undefined;
type ValidName = NonNullable<Name>;
// ValidName is string
ReturnType<>
ReturnType<> infers the return type of a function. This lets the function be the single source of truth for the shape of its output.
function getUser() {
return { id: 1, name: 'John Doe' };
}
type User = ReturnType<typeof getUser>;
// User is { id: number; name: string; }
InstanceType<>
InstanceType<> extracts the instance type from a constructor function. Useful when passing class constructors around and you need to reference the type of what they produce.
class User {
constructor(public id: number, public name: string) {}
}
type UserInstance = InstanceType<typeof User>;
// UserInstance is User
Conclusion
Utility types are one of the more practical parts of TypeScript's type system. Partial<> and Omit<> alone will cover the majority of cases you'll encounter when modeling API inputs and public data shapes. The others are worth knowing so you can recognize when they apply rather than writing redundant type definitions by hand.
The most common mistake is writing a new interface when a utility type applied to an existing one would do the job with less code and better coupling to the source type.