Lesson 9 · Module 1.4 — Core type vocabulary
Partial, Record, Pick, Omit, Exclude, ExtractReal-world code leans on these built-ins constantly — Partial<Record<Exclude<…>, string>>
shows up verbatim — yet they’re rarely spelled out. Each is a small type-level function: it takes a type
and returns a transformed one. Learn the six everyday ones and you can read most library signatures.
| utility | reads as | example → result |
|---|---|---|
| Partial<T> | every property optional | Partial<{a:1}> → {a?:1} |
| Record<K,V> | object with keys K, values V | Record<string,number> |
| Pick<T,K> | keep only keys K of T | Pick<User,"id"> |
| Omit<T,K> | drop keys K from T | Omit<User,"password"> |
| Exclude<U,X> | remove members X from union U | Exclude<"a"|"b","b"> → "a" |
| Extract<U,X> | keep members of U assignable to X | Extract<"a"|1,string> → "a" |
Exclude and Extract are a complementary pair, and the thing to notice is that
their second argument is a type used as a filter — read it “…members
assignable to X,” not “the member literally named X.” Extract<U, X> keeps the
members of U that match X; Exclude<U, X> drops them:
Extract<"a" | 1 | "b", string> // "a" | "b" — keeps the members that ARE strings
Exclude<"a" | 1 | "b", string> // 1 — drops those, keeps the rest
So in Extract<keyof T, string> the string is a category, not a
key name. keyof T can contain number and symbol keys as well as string ones, and
Extract<…, string> narrows it to just the string-named keys — the form many mapped and
template-literal types need, since those require a key that extends string.
Omit vs ExcludeThey sound interchangeable but operate on different things — and reading them confused is a common slip:
Omit<T, K> | Exclude<U, X> | |
|---|---|---|
| operates on | an object type T | a union U |
| removes | the properties named in K | the union members in X |
| returns | a smaller object type | a smaller union |
type User = { id: number; name: string; password: string };
type Public = Omit<User, "password">; // { id: number; name: string } — drops a PROPERTY
type Visible = Exclude<keyof User, "password">; // "id" | "name" — drops a UNION member
So Omit works on the object; Exclude works on the union of
its keys. In fact Omit<T, K> is built on top of Exclude — it
exists exactly so you don’t have to hand-roll Pick<T, Exclude<keyof T, K>>.
A well-known i18n type nests three at once. Read it inside-out:
Partial<Record<Exclude<PluralRule, "other">, string>>
// └─ Exclude: drop "other" from the union of plural rules
// └──── Record: build an object keyed by those remaining rules, values string
// Partial: ...and make every one of those keys optional
Three small functions chained: filter a union, build an object from it, loosen it. That is the whole pattern behind most “scary” utility expressions.
DeepPartialA common gotcha: Partial<T> only loosens the top level — nested
objects stay required. DeepPartial recurses to fix that:
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
Read it as: “for every key, make it optional (?); if its value is an object, apply
DeepPartial again, otherwise keep the value type.” The recursion — DeepPartial
calling itself on T[P] — is what reaches all the way down. You won’t often write
this, but you’ll read it (and its array-handling cousins) in real codebases.
Omit takes an object and removes properties;
Exclude takes a union and removes members. If the
first argument is { … } it’s Omit; if it’s A | B | C it’s
Exclude.
Recall, don’t re-read. (Answers reveal on click.)
Omit and Exclude is that Omit…Partial<User> where User has a nested address object will…Record<string, number>?The canonical list is the handbook’s
Utility Types page —
it gives the one-line definition of every one of these. Best way to feel it: paste
Omit<User,"password"> and Exclude<keyof User,"password"> into the
TS Playground and hover both results side by side.
Met a nested utility expression you can’t unwind? Read it inside-out, one utility at a time. Next lesson (1.5): tuples vs arrays — why a function’s parameter list is a tuple.