In TypeScript, what do `Pick<T, K>` and `Omit<T, K>` produce, and why derive a type with them instead of hand-writing a second interface?
answer
- one keeps, one drops
- subset of an existing shape
- single source of truth
- modifiers ride along
- erased — nothing at runtime
basics
~20 sPick<T, K> builds an object type containing only the properties of T named in K; Omit<T, K> contains every property of T except those. Deriving keeps one source of truth, so editing T updates both types automatically.
solid answer
~50 sBoth are built-in utility types that derive a new object type from an existing one. `Pick<User, "id" | "name">` keeps exactly those two properties; `Omit<User, "passwordHash">` keeps everything else. They are complements — you reach for whichever names fewer keys. The reason to derive rather than declare a second interface by hand is drift: a hand-written `PublicUser` silently goes stale the moment someone adds a field to `User`, whereas the derived type re-computes on every compile, so a new required field shows up in the response type (or is deliberately excluded) without anyone remembering to edit two places. They also carry the property's original type and its `?` and `readonly` modifiers across, so you are copying the real shape, not an approximation of it. Everything happens at compile time — the emitted JavaScript contains no trace of either.
code
typescript · 12 linesinterface User {
id: string;
name: string;
email: string;
passwordHash: string;
}
type PublicUser = Omit<User, "passwordHash">;
type UserCard = Pick<User, "id" | "name">;
const card: UserCard = { id: "u1", name: "Ada" };
console.log(card.name);go deeper
Be able to say plainly that Pick keeps the listed properties and Omit removes them, and to read Pick<User, "id" | "name"> out loud correctly. Mention that both are compile-time only.
Explain that Pick is the mapped type { [P in K]: T[P] } and Omit is Pick<T, Exclude<keyof T, K>>, and that optional and readonly modifiers survive because Pick is homomorphic.
Show that the derived type is a promise, not a filter: the field is still on the wire unless code removes it. Talk about drift — why a derived response type catches a newly added entity field and a hand-written one does not.
Own the coupling question. Deriving a public contract from a persistence entity means every storage rename becomes a wire-format change; be ready to say where you draw the line between one shape seen through a window and two contracts that merely look alike today.
## What they are `Pick` and `Omit` are two of TypeScript's built-in *utility types* — generic type aliases that ship in the compiler's standard library and take an existing type as input to produce a new one. Neither is a function or a runtime helper: they are instructions to the type checker, and the JavaScript the compiler emits contains nothing at all from them. `Pick<T, K>` produces the object type made of just the properties of `T` whose names appear in `K`. `K` is usually a union of string literal types: ```ts interface User { id: string; name: string; email: string; passwordHash: string } type UserCard = Pick<User, "id" | "name">; // { id: string; name: string } ``` `Omit<T, K>` is the complement — everything in `T` *except* the named keys: ```ts type PublicUser = Omit<User, "passwordHash">; // { id, name, email } ``` The two are interchangeable in what they can express; you choose by which side is shorter to write. Dropping one secret field from a fifteen-field entity is an `Omit`; lifting two display fields out of it is a `Pick`. ## How they are defined `Pick` is a mapped type — it walks a set of keys and builds a property for each one: ```ts type Pick<T, K extends keyof T> = { [P in K]: T[P] }; ``` Read it as: for each name `P` in the union `K`, give the result a property `P` whose type is `T[P]`, the type that property had in `T`. The constraint `K extends keyof T` says the keys you ask for must actually be keys of `T`. `Omit` is not written from scratch; it is expressed in terms of `Pick`: ```ts type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>; ``` It takes all of `T`'s keys, removes the ones you named, and picks what is left. Note the looser constraint on its `K` — that difference has consequences, and it is a favourite interview follow-up. ## Modifiers survive Because `Pick` maps over a key set derived from `T`, the compiler treats it as *homomorphic*: optional and `readonly` markers come across with the property rather than being flattened away. ```ts interface Row { readonly id: string; note?: string; total: number } type Trimmed = Omit<Row, "total">; // { readonly id: string; note?: string } ``` That matters — a hand-written replacement almost always loses one of these by accident, turning an optional field required or a `readonly` field mutable. ## Why derive at all The real argument is maintenance, not brevity. Consider a `User` entity and a hand-written response interface listing the safe fields. Six months later someone adds `internalNotes: string` to `User`. The hand-written interface compiles exactly as before; nothing tells you it is now incomplete or, worse, that the serializer is shipping a field the type never described. The derived `Omit<User, "passwordHash">` re-computes and picks up the new field immediately, so the change is visible where it matters. The same argument drives component props and form models: define the domain shape once, then express every view of it as a subset. Readers can see at a glance that `Pick<Order, "id" | "status">` *is* part of `Order`, which a structurally identical standalone interface never communicates. ## The costs to be aware of Deriving creates coupling in the other direction too. If the source type is a persistence entity and the derived type is a public contract, a rename in the database layer becomes a change to your wire format — which may be exactly what you do *not* want. Deriving is right when the two shapes are genuinely the same data seen through a smaller window, and wrong when they are two contracts that merely resemble each other today. A second cost is readability at depth. `Omit<Pick<Omit<T, "a">, "b" | "c">, "b">` is technically fine and humanly unreadable; editor tooltips show the alias rather than the resolved shape, so a reader has to evaluate it in their head. One or two layers is idiomatic; a tower of them is a smell. ## Runtime reality Neither utility removes anything from an actual object. `Omit<User, "passwordHash">` describes a value without that field; it does not delete it. If you assign a full `user` object to a variable of that type, the property is still there at runtime and will still be serialized by `JSON.stringify`. Excess property checks only fire on fresh object literals, not on variables. Stripping the field is a runtime job — a `delete`, a rest destructure, or an explicit mapping function — and the type is only the promise that you did it.
- Does `Omit<User, "passwordHash">` actually stop the password hash from reaching the client?No. It is a compile-time description only; the emitted JavaScript is unchanged and the property is still on the object at runtime, so `JSON.stringify` will happily serialize it. Excess property checks only fire on fresh object literals, not on a variable assigned to the derived type. Removing the field needs real code — a rest destructure, a `delete`, or an explicit mapping function.
- How would you express `Omit<T, K>` using only `Pick`?`Omit` is literally defined that way: `Pick<T, Exclude<keyof T, K>>`. You take the full key union with `keyof T`, subtract the keys you want gone, and pick the remainder. Knowing this shape explains most of `Omit`'s behaviour, including why it never complains about a key that is not in `T`.
- What happens to optional and readonly markers when you Pick a property?They come across intact. `Pick` is a homomorphic mapped type — it maps over a key set constrained to `keyof T` — so the compiler preserves each property's `?` and `readonly` rather than resetting them. `Pick<{ readonly a?: number }, "a">` is still `{ readonly a?: number }`. A hand-written replacement is where those markers usually get lost.
Pick is a guest list, Omit is a bouncer's ban list — both end up describing who is in the room, and you write whichever list is shorter.
saying these in an interview costs you the question
- Thinking Omit deletes the property from the object at runtime
- Believing Pick and Omit copy methods into a callable class type
- Assuming derived types lose optional and readonly markers
- Writing a parallel interface by hand and keeping both in sync manually
- Calling them functions you import from a library