Utility Types
Utility types are generic types built into TypeScript's standard library that transform an existing type into a new, related type — making optional/required/readonly/subset variations of a type without redeclaring its shape from scratch. They are not special compiler syntax; they're ordinary generic types you could write…
🎯 What you'll learn
- What Utility Types Are
Partial<T>— Every Property OptionalRequired<T>— Every Property MandatoryReadonly<T>— Every Property Read-OnlyPick<T, K>— Select a Subset of Properties
1What Utility Types Are
Utility types are generic types built into TypeScript's standard library that transform an existing type into a new, related type — making optional/required/readonly/subset variations of a type without redeclaring its shape from scratch. They are not special compiler syntax; they're ordinary generic types you could write yourself (full proof in "Under the Hood").
2Partial<T> — Every Property Optional
interface User { id: number; name: string; email: string; }
function updateUser(id: number, changes: Partial<User>) {
// changes might only include SOME of User's properties
}
updateUser(1, { name: "New Name" }); // ✅ — only name provided, rest omittedExtremely common for "patch"-style update functions where callers only supply the fields they want to change.
3Required<T> — Every Property Mandatory
interface Config { host?: string; port?: number; }
function connect(config: Required<Config>) {
// both host and port are GUARANTEED present here
}
// connect({ host: "localhost" }); // ❌ port is still required now
connect({ host: "localhost", port: 8080 }); // ✅The inverse of Partial — turns every optional property into a required one.
4Readonly<T> — Every Property Read-Only
interface Point { x: number; y: number; }
const frozen: Readonly<Point> = { x: 1, y: 2 };
// frozen.x = 5; // ❌ error: x is readonlyCompile-time only, like the readonly modifier itself (see the Interfaces topic) — no runtime enforcement.
5Pick<T, K> — Select a Subset of Properties
interface User { id: number; name: string; email: string; password: string; }
type PublicUser = Pick<User, "id" | "name">; // { id: number; name: string }K must be a union of literal keys that actually exist on T — Pick<User, "id" | "typo"> would be a compile error.
6Omit<T, K> — Exclude a Subset of Properties
type PublicUser = Omit<User, "password">; // everything EXCEPT passwordThe inverse of Pick — instead of listing what to keep, list what to remove.
7Record<K, V> — Build an Object Type From a Key Set and a Value Type
type Role = "admin" | "editor" | "viewer";
type Permissions = Record<Role, boolean>;
// { admin: boolean; editor: boolean; viewer: boolean }
const perms: Permissions = { admin: true, editor: true, viewer: false };Record is the standard way to build a dictionary-shaped type where every key from a known set maps to the same value type — far more precise than a generic index signature when the key set is closed and known.
8Composing Utility Types
interface User { id: number; name: string; email: string; password: string; }
type UserUpdate = Partial<Omit<User, "id">>; // every field optional EXCEPT id is entirely excludedUtility types compose naturally — this is one of their biggest strengths — because each one just takes a type and returns another type, so they nest exactly like function calls.
1Exclude<T, U> — Remove Union Members
type AllRoles = "admin" | "editor" | "viewer" | "banned";
type ActiveRoles = Exclude<AllRoles, "banned">; // "admin" | "editor" | "viewer"Removes every member of the union T that is assignable to U.
2Extract<T, U> — Keep Only Matching Union Members
type Mixed = string | number | (() => void);
type OnlyFunctions = Extract<Mixed, Function>; // () => voidThe inverse of Exclude — keeps only the members of T that ARE assignable to U.
3NonNullable<T> — Strip null and undefined
type MaybeName = string | null | undefined;
type DefiniteName = NonNullable<MaybeName>; // string4ReturnType<T> and Parameters<T> — Extract Function Signature Pieces
function createUser(name: string, age: number) {
return { id: 1, name, age };
}
type User = ReturnType<typeof createUser>; // { id: number; name: string; age: number }
type CreateUserArgs = Parameters<typeof createUser>; // [name: string, age: number]Extremely useful for deriving types from existing functions instead of duplicating a shape by hand — if createUser's implementation changes, User and CreateUserArgs stay automatically correct.
5Awaited<T> — Unwrap a Promise Type
async function fetchUser() {
return { id: 1, name: "Ana" };
}
type FetchedUser = Awaited<ReturnType<typeof fetchUser>>; // { id: number; name: string }
// without Awaited, ReturnType<typeof fetchUser> would be Promise<{ id: number; name: string }>Under the HoodUtility Types Are Just Ordinary Type Definitions▾
None of these are special compiler syntax — every single one is defined in TypeScript's own lib.es5.d.ts (and related lib files), shipped as part of the compiler, using the exact same mapped-type and conditional-type features covered in their own topics. You could write every one of these yourself, and in fact this is genuinely their real source:
type Partial<T> = {
[P in keyof T]?: T[P];
};
type Required<T> = {
[P in keyof T]-?: T[P];
};
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};
type Record<K extends keyof any, T> = {
[P in K]: T;
};
type Exclude<T, U> = T extends U ? never : T;
type Extract<T, U> = T extends U ? T : never;
type NonNullable<T> = T extends null | undefined ? never : T;
type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;Reading Partial<T> for real, now that the pieces are covered elsewhere
[P in keyof T]?: T[P] is a mapped type (its own topic): keyof T gets every property name of T as a union of literal types, P in <that union> iterates each one, T[P] looks up that property's original type (an indexed access type), and the ? marks each resulting property optional. Nothing here is compiler magic — it's the mapped-types feature, applied once, in the standard library, and given a convenient name.
Required<T>'s -? is a mapped-type modifier, not a typo
The -? you see in the real Required<T> definition removes the optional modifier from every property (as opposed to plain ? which adds it) — full detail on +/- modifiers belongs to the Mapped Types topic, but it's worth seeing here as direct proof that Required is Partial's exact structural opposite, implemented with one symbol's difference.
Why this matters practically
Because utility types are ordinary type definitions, you can read their real source directly in your editor (jump-to-definition on Partial in almost any TypeScript-aware editor lands you in lib.es5.d.ts), and — more importantly — you can write your own domain-specific equivalents using the exact same techniques once you've covered Mapped Types, Conditional Types, and the infer keyword. DeepPartial<T>, DeepReadonly<T>, and similar community-standard "missing" utility types that aren't built in are written using precisely this same toolkit.
⚠️ Common Pitfalls
- Assuming
Partial/Required/Readonlyare deep — all three are shallow:Partial<{ a: { b: number } }>makesaoptional, buta's nestedbis still required; deep variants need custom recursive mapped types (own construction, or a library liketype-fest). - Confusing
Exclude/Extractdirection —Excluderemoves matches,Extractkeeps matches; easy to mix up since both take the same two type arguments. - Using
Pick/Omitwith a key that doesn't exist onT—Pickerrors immediately (itsK extends keyof Tconstraint catches it);Omithistorically did NOT have this constraint for backward-compatibility reasons in some TS versions, so a typo'd key can silently do nothing instead of erroring — always double checkOmitkey names carefully. - Reaching for
ReturnType/Parameterson an overloaded function — they only reflect the LAST overload signature by default, which can be surprising for heavily overloaded functions.
📋 Quick Reference
Partial<T> / Required<T> / Readonly<T> → shallow, per-property modifier changes
Pick<T,K> / Omit<T,K> → subset selection
Record<K,V> → build a dictionary shape
Exclude<T,U> / Extract<T,U> → union filtering (remove / keep matches)
NonNullable<T> → strip null | undefined
ReturnType<T> / Parameters<T> / Awaited<T> → derive types FROM existing functions/promises🎤 Interview Q&A
Q1Name common utility types and their uses.core▾
Partial<T> (all optional, e.g. update DTOs), Required<T>, Readonly<T>, Pick<T, K>, Omit<T, K>, Record<K, V>, Exclude<U, X>, Extract<U, X>, NonNullable<T>, ReturnType<F>, Parameters<F>, Awaited<T>.
- Pick vs Omit? Pick selects keys; Omit removes them.
Q2How would you type a PATCH request body?core▾
Partial<Pick<User, 'name' | 'email'>> limits updatable fields and makes them optional.
- Why not Partial<User>? It would allow updating id, role, etc.
Q3How are utility types implemented?core▾
Mostly with mapped and conditional types: type Partial<T> = { [K in keyof T]?: T[K] }, type Exclude<T, U> = T extends U ? never : T.
- Implement ReturnType?
T extends (...args: any) => infer R ? R : never.
Q4What's the pitfall of Omit with unions?senior▾
Omit isn't distributive: on a union it collapses to common keys, losing discriminants. Use a distributive version: type DistributiveOmit<T, K> = T extends any ? Omit<T, K> : never.
- Why? keyof a union gives only common keys.
Q5How do Awaited and ReturnType help with async functions?core▾
Awaited<ReturnType<typeof fetchUser>> gives the resolved type of an async function's result, avoiding duplicated type definitions.
- Parameters example?
Parameters<typeof fn>[0]for the first argument type.
🛠️ Practice exercises
Each exercise is a file in this topic folder. Code exercises are self-checking: run them and make the checks pass.