Skip to main content

Utility type pick in TypeScript

Pick<T, K> constructs a new type by selecting only the keys you name from T, keeping their original types and dropping everything else.

Theory

TL;DR

  • Think of it like ordering from a menu: you get exactly the dishes you picked, with full recipes intact, nothing extra.
  • Pick<User, 'id' | 'name'> gives you { id: number; name: string }email and the rest are gone.
  • Keys must exist in T or TypeScript errors at compile time. No runtime filtering, type-check only.
  • Use Pick for 1-5 related props. For many exclusions, reach for Omit instead.

Quick example

ts
interface User { id: number; name: string; age: number; email: string; } // Picks only 'id' and 'name' — age and email are gone type UserPreview = Pick<User, 'id' | 'name'>; const preview: UserPreview = { id: 1, name: 'Alice' }; // OK const invalid: UserPreview = { id: 1, name: 'Alice', age: 30 }; // Error

UserPreview only knows about id and name. Passing age is a compile-time error, not a runtime one.

Key difference

Pick creates a shallow subset. It copies exact types from T without recursing into nested objects. If T has { address: { street: string } }, then Pick<T, 'address'> keeps the whole nested address object intact — it just drops other top-level keys. You cannot trim nested fields with a single Pick call.

When to use

  • API response subset: expose only safe public fields from a full entity (for example, drop passwordHash before sending data to the client).
  • React component props: when a component needs 2-4 fields from a larger domain type, Pick keeps the props in sync with the source type automatically.
  • Form inputs: partial update DTOs where you only accept specific fields.
  • Event payloads: pick specific fields from a request body type.

Avoid Pick if you need more than 5-6 keys or need to transform types. A mapped type handles those cases better.

How TypeScript handles Pick internally

Pick is defined as a mapped type: type Pick<T, K extends keyof T> = { [P in K]: T[P] }. The compiler iterates through K, looks up each key's type in T via indexed access, and builds a new object type. No runtime code is produced — Pick is erased entirely after type checking.

That also means Pick has zero cost at runtime. The compiled JavaScript is identical whether you annotate with User or Pick<User, 'id'>.

Common mistakes

Assuming runtime filtering

ts
const fullUser = { id: 1, name: 'Alice', secret: 'pass123' }; const picked: Pick<User, 'id' | 'name'> = fullUser; // Compiles fine! // But secret is still in the object at runtime

Pick does not strip properties from actual objects. It only constrains what TypeScript lets you type-check against. To project at runtime, build the object manually: const picked = { id: fullUser.id, name: fullUser.name }.

Using a wide string as K

ts
const key = 'id' as string; type Wrong = Pick<User, typeof key>; // Error: 'string' not assignable to keyof User

K must be string literal types. Use 'id' | 'name' or a keyof subset, not the broad string type.

Forgetting that optional props stay optional

ts
interface User { id: number; name?: string; } type Picked = Pick<User, 'name'>; // { name?: string } — still optional

If you need name required after picking, wrap it: Required<Pick<User, 'name'>>.

Nested types need chained indexed access

ts
interface User { details: { age: number }; } // This gives { details: { age: number } } — not just age type A = Pick<User, 'details'>; // To get only age from details: type B = Pick<User['details'], 'age'>; // { age: number }

Real-world usage

  • React / Next.js: Pick<User, 'id' | 'name'> as list item component props.
  • Express / NestJS: Pick<User, 'name' | 'email'> for update DTOs in controllers.
  • Redux Toolkit: Pick<Action, 'type' | 'payload'> for typed action creators.
  • tRPC: procedure inputs typed as Pick<Entity, PublicKeys>.

One thing I see teams miss consistently: they annotate the return type with Pick but forget to project the object at runtime. Sensitive fields end up in the response even though the TypeScript type looks correct.

Follow-up questions

Q: What does Pick<User, keyof User> produce?
A: The same type as User. Selecting all keys gives back the original shape.

Q: Can you Pick from a union type?
A: Yes, Pick<User | Admin, 'id' | 'name'> distributes across the union. The result includes the common keys from both members.

Q: What is Pick<T, never>?
A: An empty object type {}. Selecting no keys produces a type that only accepts an empty object.

Q: (Senior) Implement Pick from scratch.
A: type MyPick<T, K extends keyof T> = { [P in K]: T[P] }. The constraint K extends keyof T is what makes non-existent keys a compile-time error.

Q: How does Pick differ from writing the type manually?
A: The resulting structures are identical, but Pick stays in sync automatically. If you add a field to User and it matches a picked key, every derived type updates without touching it manually.

Examples

Basic: exposing public fields from a User

ts
interface User { id: number; name: string; email: string; passwordHash: string; // never expose this isAdmin: boolean; } type PublicProfile = Pick<User, 'id' | 'name' | 'email'>; function getPublicProfile(user: User): PublicProfile { // Manual projection — Pick constrains the type, not the runtime object return { id: user.id, name: user.name, email: user.email, }; }

passwordHash and isAdmin are excluded from the return type. The function must project explicitly — TypeScript does not strip them automatically.

Intermediate: React component props from a domain model

ts
interface User { id: number; name: string; email: string; role: 'user' | 'admin'; createdAt: Date; } type UserCardProps = Pick<User, 'id' | 'name' | 'role'>; const UserCard = ({ id, name, role }: UserCardProps) => ( <div> {id}{name} ({role}) </div> ); // Only the picked props are required <UserCard id={1} name='Bob' role='admin' />;

UserCardProps stays in sync with User. If you rename role in the interface, TypeScript flags every Pick referencing it. That is the main benefit over writing props by hand.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?