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.
| Utility | Transforms |
|---|---|
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 fieldsDraft / create patterns
type UserDraft = Partial<User> & Pick<User, "email">;
// email required for create, other fields optional during draftInterview:
Partialmodels 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 | numberReal-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 keysNonNullable
Definition
type MaybeUser = User | null | undefined;
type DefiniteUser = NonNullable<MaybeUser>; // UserUseful 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>>>; // numberUse 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 layerSenior 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 snapshotsBest mental model
Utility types = type-level functions
Input: existing type → Output: transformed type
Always derive from source of truthPractical 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