Topics
v6.0.0
On this page · sections

Utility Types

TypeScript ships built-in utility types that transform existing types. Senior frontend engineers reach for them daily in props, API layers, form state, and hook return types — they keep types derived from a single source of truth.

UtilityTransforms
Partial<T>All properties optional
Required<T>All properties required
Readonly<T>All properties readonly
Pick<T, K>Subset of keys
Omit<T, K>Exclude keys
Record<K, V>Object with keys K and values V
Exclude<T, U>Remove union members assignable to U
Extract<T, U>Keep union members assignable to U
NonNullable<T>Remove null and undefined
ReturnType<F>Function return type
Parameters<F>Function parameter tuple
Awaited<T>Unwrap Promise layers

Partial and Required

Definition

type User = { id: string; name: string; email: string; }; type UserPatch = Partial<User>; // { id?: string; name?: string; email?: string } type CompleteUser = Required<User>; // all keys required (useful after defaults applied)

Real-world example — update forms

function updateUser(id: string, patch: Partial<User>) { return fetch(`/api/users/${id}`, { method: "PATCH", body: JSON.stringify(patch), }); } updateUser("1", { name: "Ada" }); // ✅ only changed fields

Draft / create patterns

type UserDraft = Partial<User> & Pick<User, "email">; // email required for create, other fields optional during draft

Interview: Partial models PATCH payloads; don’t use it for create forms where required fields must stay required.

Pick and Omit

Definition

type UserPreview = Pick<User, "id" | "name">; // { id: string; name: string } type UserWithoutEmail = Omit<User, "email">; // { id: string; name: string }

Component props from domain model

type UserCardProps = Pick<User, "name" | "email"> & { onEdit: () => void; }; function UserCard({ name, email, onEdit }: UserCardProps) { return ( <div> <h3>{name}</h3> <p>{email}</p> <button onClick={onEdit}>Edit</button> </div> ); }

Prefer Pick when the card needs a few fields; prefer Omit when the card needs everything except sensitive fields.

Omit for derived event props

type ButtonProps = React.ButtonHTMLAttributes<HTMLButtonElement>; type IconButtonProps = Omit<ButtonProps, "children"> & { icon: React.ReactNode; "aria-label": string; };

Readonly

Definition

type Config = Readonly<{ apiUrl: string; retries: number; }>; const config: Config = { apiUrl: "/api", retries: 3 }; // config.retries = 5; // ❌ Cannot assign to 'retries'

Immutable config objects and frozen props benefit from Readonly. For deep immutability, combine with a custom DeepReadonly (see Mapped & Conditional Types).

type Props = Readonly<{ user: User; onSave: (user: User) => void; }>;

Record

Definition

type Role = "admin" | "editor" | "viewer"; type Permissions = Record<Role, string[]>; const perms: Permissions = { admin: ["read", "write", "delete"], editor: ["read", "write"], viewer: ["read"], };

Lookup tables and dictionaries

type Status = "idle" | "loading" | "success" | "error"; const LABELS: Record<Status, string> = { idle: "Idle", loading: "Loading…", success: "Done", error: "Failed", }; type ErrorMessages = Record<string, string>;

Record<K, V> requires every key in K to be present — ideal for exhaustive maps over finite unions.

Exclude and Extract

Definition

Filter union members by assignability:

type T = "a" | "b" | "c"; type AB = Exclude<T, "c">; // "a" | "b" type OnlyB = Extract<T, "b">; // "b" type All = string | number | boolean; type Primitives = Exclude<All, boolean>; // string | number

Real-world example — event names

type DOMEvent = "click" | "focus" | "blur" | "custom"; type NativeEvent = Exclude<DOMEvent, "custom">; // "click" | "focus" | "blur"

Omit alternative with Pick

type WithoutId<T> = Pick<T, Exclude<keyof T, "id">>; // equivalent to Omit<T, "id"> for string keys

NonNullable

Definition

type MaybeUser = User | null | undefined; type DefiniteUser = NonNullable<MaybeUser>; // User

Useful after filtering in type-level utilities:

type NonNullableFields<T> = { [K in keyof T]: NonNullable<T[K]>; };

ReturnType and Parameters

Definition

Extract types from existing functions — useful when you don’t control the function’s type definition.

async function fetchUser(id: string) { return { id, name: "Ada" } as const; } type UserResult = Awaited<ReturnType<typeof fetchUser>>; // { readonly id: string; readonly name: "Ada" } type FetchUserParams = Parameters<typeof fetchUser>; // [id: string]

Real-world example — infer hook return type

function useAuth() { return { user: null as User | null, login: (email: string) => {} }; } type Auth = ReturnType<typeof useAuth>; // { user: User | null; login: (email: string) => void }

Interview: ReturnType<typeof fn> keeps client code in sync when the hook’s implementation changes.

Awaited

Definition

Unwraps Promise (including nested promises):

type A = Awaited<Promise<string>>; // string type B = Awaited<Promise<Promise<number>>>; // number

Use with ReturnType on async functions:

type Data = Awaited<ReturnType<typeof fetchDashboard>>;

Choosing the right utility

Need a few fields from a model → Pick Need everything except secrets → Omit PATCH / partial update → Partial After merging defaults → Required Map over a finite key union → Record Filter a string union → Exclude / Extract Sync with function/hook → ReturnType / Parameters Async return type → Awaited<ReturnType<...>>

Anti-patterns

Partial everywhere on create DTOs → loses required-field safety Duplicating Pick manually → drifts from source type any instead of ReturnType → breaks when implementation changes Record with non-literal key type → becomes { [key: string]: V } — often too loose Omit to "hide" fields from API → runtime still sends them; fix at serialization layer

Senior Interview Summary

When to reach for each

Partial<T> → PATCH / draft state / optional overrides Required<T> → after merging defaults Pick / Omit → component props from domain types Record<K,V> → maps, permission tables, label dictionaries Exclude / Extract → filter union members ReturnType → infer from existing functions/hooks Awaited → unwrap Promise return types Readonly → config / immutable snapshots

Best mental model

Utility types = type-level functions Input: existing type → Output: transformed type Always derive from source of truth

Practical rule

Derive types from source of truth → Pick/Omit/ReturnType Transform for API contracts → Partial/Required Never hand-copy field lists → utility types stay in sync Custom utilities in shared types → built-ins in feature code

Last updated on