Utility types keep related types in sync from one source of truth instead of copy-pasting shapes.
TypeScript ships generic utility types that transform other types. Learning a dozen of them removes most hand-written duplication.
Reshaping objects:
Partial<T>— all properties optional (great for update/patch payloads).Required<T>— remove all?.Readonly<T>— all properties readonly (shallow).Pick<T, "a" | "b">— keep only those keys.Omit<T, "password">— drop keys. NoteOmitdoesn't check that the keys exist; a typo silently omits nothing.Record<K, V>— an object with keysKand valuesV, e.g.Record<"en" | "fr", string>requires both keys.
Working with unions:
Exclude<U, X>/Extract<U, X>— remove or keep union members assignable toX.NonNullable<T>— removenullandundefined.
Working with functions and promises:
ReturnType<typeof fn>andParameters<typeof fn>— the return type and parameter tuple of a function type. Note thetypeof: utilities take types, not values.Awaited<T>— the value a promise resolves to, unwrapping nested promises.
Strings: Uppercase, Lowercase, Capitalize, Uncapitalize transform string literal types.
A typical pattern: define one main type and derive the rest — type NewUser = Omit<User, "id" | "createdAt">, type UserPatch = Partial<NewUser>. When User changes, everything follows.
None of these are magic: they're written with mapped and conditional types in lib.es5.d.ts, which you'll learn to write yourself in the Advanced lessons. Hover over one in your editor to see its definition.
Example
interface User {
id: number;
name: string;
email: string;
role: "admin" | "member" | "guest";
createdAt: Date;
}
type NewUser = Omit<User, "id" | "createdAt">;
type UserPatch = Partial<NewUser>;
type PublicUser = Pick<User, "id" | "name">;
type Staff = Exclude<User["role"], "guest">; // "admin" | "member"
type Labels = Record<User["role"], string>;
async function loadUser(id: number): Promise<PublicUser> {
return { id, name: "Ada" };
}
type Loaded = Awaited<ReturnType<typeof loadUser>>; // PublicUser
type LoadArgs = Parameters<typeof loadUser>; // [id: number]
const patch: UserPatch = { email: "ada@example.com" };
const labels: Labels = { admin: "Admin", member: "Member", guest: "Guest" };
const role: Staff = "member";
const args: LoadArgs = [1];
loadUser(...args).then((u: Loaded) => console.log(u.name, patch, labels[role]));Remember
- Derive types from one source with Pick, Omit, Partial, Record
Exclude/Extract/NonNullablefilter unionsReturnType<typeof f>,Parameters<typeof f>,Awaited<T>read types off functions and promises
Watch out for
- Writing
ReturnType<fn>instead ofReturnType<typeof fn>— utilities take types, not values.