Not Everything Should Be Visible: Understanding Pick in TypeScript
Pick<T, K> allows you to derive a focused view of a type, selecting only the properties you need while keeping everything connected to the original structure.
Thereâs a subtle habit we develop when working with types.
We define something once⌠and then quietly start redefining parts of it elsewhere.
type User = {
id: number
name: string
email: string
password: string
}At first, this feels complete.
It represents a user, fully.
But then reality steps in.
You donât always want the full picture.
When displaying a profile, you donât need the password.
When listing users, maybe you only care about names.
When sending data to the frontend, you deliberately hide certain fields.
And suddenly, youâre not working with âa userâ anymore.
Youâre working with a version of a user.
The Quiet Start of Duplication
The natural move is simple.
You write another type.
type PublicUser = {
id: number
name: string
}It works.
Itâs clean.
But it introduces something easy to ignore.
Youâve just copied part of an existing structure.
And copying always comes with a cost.
If User evolves, this type doesnât automatically follow.
If a field changes type, you have to remember to update it here too.
Over time, these small duplications start drifting apart.
Not immediately.
But gradually.
From Copying to Selecting
This is where Pick starts to matter.
type PublicUser = Pick<User, "id" | "name">At a glance, nothing looks different.
The resulting shape is exactly the same.
But the meaning changes completely.
Youâre no longer defining a new structure.
Youâre selecting part of an existing one.
That shift is small in syntax.
But large in intention.
A Different Way to Think About Types
Without Pick, types feel like isolated definitions.
With Pick, they start to feel connected.
Instead of saying:
âThis is another typeâ
Youâre saying:
âThis is a view of that typeâ
That idea matters.
Because now your types arenât independent pieces.
They are relationships.
And relationships are easier to maintain than duplicates.
What Pick Actually Does
Under the surface, Pick is very direct.
type Pick<T, K extends keyof T> = {
[P in K]: T[P]
}It iterates over a set of keys and keeps their types exactly as they are.
Nothing more.
Nothing less.
So it doesnât transform the data.
It filters it.
It defines a boundary.
âOnly these parts are allowed throughâ
Why the Constraint Matters
You canât pick something that doesnât exist.
Pick<User, "id" | "unknown"> // errorAt first, this might feel restrictive.
But itâs actually protective.
It ensures that every derived type remains grounded in reality.
You can reduce.
You can reshape.
But you canât invent.
And that keeps your system consistent.
The Trade-off Behind the Simplicity
Pick is explicit.
You have to name every field you want.
type PublicUser = Pick<User, "id" | "name">If later you need email, you must add it.
That might feel like extra work.
But it carries a benefit.
It forces clarity.
Youâre always stating:
âThese are the exact fields I care aboutâ
Nothing more slips in accidentally.
Nothing is included by default.
A Subtle Shift in Perspective
Two types can look identical:
type A = {
id: number
name: string
}
type B = Pick<User, "id" | "name">But they donât mean the same thing.
The first stands alone.
The second points back to its origin.
And that connection changes how your code evolves.
Because one is a copy.
The other is a reference.
The Bigger Idea
Pick exists because sometimes the problem isnât:
âWhat is this thing?â
But:
âWhich parts of this thing do I need right now?â
It turns types into something more flexible.
Not by loosening them like Partial.
But by narrowing them with intention.
And thatâs the deeper role it plays.
Not just selecting properties.
But expressing perspective.
Because in most systems, youâre not dealing with one fixed shape.
Youâre dealing with many views of the same reality.