TypeScript Utility Types
Built-in helpers like Partial, Pick, Omit, and Record that transform existing types.
3 min read
TypeScript ships a set of built-in utility types — generic types that take an existing type and produce a new, transformed version of it. Instead of hand-writing a slightly different version of an interface every time you need one, utility types derive it directly from a single source of truth.
Given this base type, used throughout the examples below:
interface User {
id: number;
name: string;
email: string;
age: number;
}Partial<T> — make every property optional
function updateUser(id: number, changes: Partial<User>) {
// changes can include any subset of User's properties
}
updateUser(1, { name: "New Name" }); // fine -- only name provided
updateUser(1, { name: "New", age: 30 }); // also fineWithout Partial, changes: User would require every field on every call, which doesn't match how a partial update actually works.
Pick<T, K> — keep only specific properties
type UserPreview = Pick<User, "id" | "name">;
// { id: number; name: string }Useful for deriving a smaller, focused type — like the fields needed for a list view — directly from a larger canonical type, without duplicating and manually keeping them in sync.
Omit<T, K> — keep everything except specific properties
type UserWithoutEmail = Omit<User, "email">;
// { id: number; name: string; age: number }Omit is Pick's complement — instead of listing what to keep, you list what to exclude. Commonly used for forms: take the full type, and omit fields the server generates (like id) that the client shouldn't submit.
type NewUser = Omit<User, "id">; // a user being created, before it has an idReadonly<T> — make every property readonly
const frozenUser: Readonly<User> = { id: 1, name: "Ada", email: "a@x.com", age: 36 };
frozenUser.name = "Grace"; // Cannot assign to 'name' because it is a read-only propertyRecord<K, V> — an object type with specific keys, all mapped to one value type
type UsersById = Record<number, User>;
const users: UsersById = {
1: { id: 1, name: "Ada", email: "a@x.com", age: 36 },
2: { id: 2, name: "Grace", email: "g@x.com", age: 45 },
};Record is the standard way to type a lookup object/dictionary — much more precise than { [key: number]: User } written manually, and just as capable.
Required<T> — the opposite of Partial
interface Options {
timeout?: number;
retries?: number;
}
function run(options: Required<Options>) {
// both timeout and retries are guaranteed present here
}Why these matter
Every one of these is built from the same generics and mapped-type mechanics covered in the previous lesson — Partial<T> is, roughly, "for every key in T, make it optional." Using the built-in versions instead of reinventing them keeps a codebase's types derived from a single source of truth (the base User interface) rather than several manually maintained variants that can silently drift out of sync as the base type evolves.
The final section of this course moves from the type system itself into applying it: typing arrays, tuples, and classes in real code.