When do you reach for Pick, Omit and Record?
Take a moment.
Say your answer out loud or write it down first — retrieval is what makes it stick. Reveal when you're ready.
Short answer
Pick<T, K> selects a subset of properties; Omit<T, K> removes specified ones; Record<K, V> builds a type keyed by K with values V. Together they compose view models, API projections and dictionaries from base types.
Detailed explanation
These three cover most day-to-day type composition. Pick extracts public-safe shapes: type PublicUser = Pick<User, 'id' | 'name'> keeps responses minimal. Omit hides sensitive fields: SafeUser = Omit<User, 'passwordHash'> — note it stays in sync automatically if new sensitive fields are added to the exclusion list explicitly. Record constructs maps: Record<string, number> for counters, Record<Role, Permission[]> ensuring every role has an entry (with literal unions as keys, missing entries become compile errors). Combining them scales well: Record<Status, Omit<ScreenProps, 'status'>> models per-status UI configs. Caveats: Omit uses Exclude under the hood so it silently ignores nonexistent keys — Pick errors on typos, making it stricter. Deep transformations need recursive mapped types beyond these built-ins.
Example
interface Article {
id: string;
title: string;
body: string;
authorIp: string;
}
type ArticleCard = Pick<Article, 'id' | 'title'>;
type PublicArticle = Omit<Article, 'authorIp'>;
type ViewsByStatus = Record<'draft' | 'published', number>;
const views: ViewsByStatus = { draft: 3, published: 12 };Interview tip
Give one concrete use per utility from real work (API projection, hiding fields, exhaustive maps) — practical framing beats textbook definitions.
Common mistakes
- Not realizing Omit won't error on keys that never existed.
- Forgetting literal-union keys in Record give exhaustiveness checking.