671 words, 4 min read

TypeScript ships with a set of built-in utility types that make working with existing types far more ergonomic. One of the most useful is Pick<Type, Keys> β€” a simple but powerful tool for carving out a subset of properties from a larger type.

What Is Pick?

Pick<Type, Keys> constructs a new type by selecting a specific set of properties (Keys) from an existing type (Type). Think of it as a projection: you have a big object shape, and you only want a slice of it.

type Pick<T, K extends keyof T> = {
[P in K]: T[P];
};

A Practical Example

Suppose you have a User type across your application:

type User = {
id: number;
name: string;
email: string;
passwordHash: string;
createdAt: Date;
role: 'admin' | 'editor' | 'viewer';
};

When building a public-facing API response, you never want to expose passwordHash. And a user profile card in the UI might only care about id, name, and email. Instead of duplicating the shape or manually re-declaring those fields, reach for Pick:

type PublicUser = Pick<User, 'id' | 'name' | 'email'>;
// Equivalent to:
// type PublicUser = {
// id: number;
// name: string;
// email: string;
// };

Now PublicUser stays in sync with User automatically. If you rename name to fullName on User, TypeScript will immediately flag any Pick references to 'name' as an error.

Real-World Use Cases

Form State

When a form only edits a subset of a model's fields, Pick keeps the shape accurate without duplication:

type UpdateProfileForm = Pick<User, 'name' | 'email'>;
function submitProfileUpdate(data: UpdateProfileForm): Promise<void> {
return api.patch('/profile', data);
}

Component Props

Vue and React components often need just a few fields from a larger model. Pick makes this explicit in the prop type:

type AvatarProps = Pick<User, 'id' | 'name'> & {
size?: 'sm' | 'md' | 'lg';
};

Service Layer Boundaries

When passing data between services, Pick enforces that only the relevant fields cross the boundary β€” a lightweight form of data encapsulation:

type EmailPayload = Pick<User, 'name' | 'email'>;
function sendWelcomeEmail(user: EmailPayload): void {
mailer.send({
to: user.email,
subject: `Welcome, ${user.name}!`,
});
}

Combining with Other Utility Types

Pick composes cleanly with other utilities. For example, Partial<Pick<...>> gives you an optional subset β€” perfect for PATCH request bodies:

type PatchUserRequest = Partial<Pick<User, 'name' | 'email' | 'role'>>;

Pick vs Omit

Pick and Omit are mirror images of each other:

Utility Approach Best when…
Pick<Type, Keys> Allowlist β€” name what you want The desired set is small
Omit<Type, Keys> Denylist β€” name what you don't want The excluded set is small

If you want all fields except one or two, Omit is more concise. If you want just a handful of fields from a large type, Pick is cleaner.

// These are equivalent when User has exactly these five fields:
type WithoutPassword = Omit<User, 'passwordHash'>;
type SafeFields = Pick<User, 'id' | 'name' | 'email' | 'createdAt' | 'role'>;
// Prefer Omit here β€” fewer keys to list

Key Takeaways

  • Pick<Type, Keys> creates a new type with only the named properties from Type.
  • The Keys argument is validated against keyof Type at compile time β€” typos are caught immediately.
  • It keeps derived types in sync with their source; renames and removals surface as errors rather than silent mismatches.
  • It composes well with Partial, Required, Readonly, and Omit for expressive, minimal type definitions.

Whenever you find yourself re-declaring a handful of fields that already exist on another type, Pick is almost certainly the right tool.