DEV Community

Cover image for Keeping TypeScript DRY with Pick and Omit
Rocky Chowdhury
Rocky Chowdhury

Posted on

Keeping TypeScript DRY with Pick and Omit

In a real-world codebase, interfaces grow. A User type might have 20+ properties covering everything from authentication to UI preferences. But not every part of the codebase needs all of them — a login form cares only about email and password, while an admin panel might need everything except password.

Without the right tools, you end up duplicating type definitions across files. That duplication is a quiet bug incubator. TypeScript's Pick and Omit utility types solve this by letting you derive focused, specialized types directly from a master interface — define each property exactly once and never repeat yourself.


The Problem: Manual Type Duplication

Start with a master User interface:

interface User {
  id:        number;
  name:      string;
  email:     string;
  password:  string;
  role:      "admin" | "user";
  createdAt: Date;
}
Enter fullscreen mode Exit fullscreen mode

Without utility types, a developer might write separate interfaces for different contexts:

// For the login form
interface LoginCredentials {
  email:    string;
  password: string;
}

// For the public profile
interface PublicProfile {
  id:   number;
  name: string;
  role: "admin" | "user";
}
Enter fullscreen mode Exit fullscreen mode

Now there are three places to update whenever the User schema changes. If email gets renamed to emailAddress, you have to remember to update LoginCredentials too. This is exactly the kind of maintenance burden DRY exists to eliminate.


Pick<T, K> — Select Only What You Need

Pick<T, K> creates a new type by selecting a subset of keys K from type T. You name the properties you want, and TypeScript builds the type from the master interface.

type LoginCredentials = Pick<User, "email" | "password">;
// Equivalent to: { email: string; password: string }

type PublicProfile = Pick<User, "id" | "name" | "role">;
// Equivalent to: { id: number; name: string; role: "admin" | "user" }
Enter fullscreen mode Exit fullscreen mode

Both LoginCredentials and PublicProfile are now derived from the master User interface. Rename email to emailAddress in User and TypeScript immediately flags every derived type that referenced the old key — no manual search required.

Use Pick when you need a small, specific subset of a larger type — especially for form inputs, API request payloads, or narrow component props.


Omit<T, K> — Exclude What You Don't Want

Omit<T, K> creates a type with everything from T except the keys listed in K. It's more convenient than Pick when you want most of the properties and only need to drop a few.

type SafeUser = Omit<User, "password">;
// Equivalent to:
// { id: number; name: string; email: string; role: "admin" | "user"; createdAt: Date }
Enter fullscreen mode Exit fullscreen mode

This pattern is common when returning user data from an API — you want the full object minus any sensitive fields.

A practical example

function getUser(id: number): Omit<User, "password" | "createdAt"> {
  // returns everything except the sensitive fields
}
Enter fullscreen mode Exit fullscreen mode

Use Omit when your derived type needs most of the parent. It's less verbose than listing every key you do want with Pick.


Pick vs Omit — When to Use Which

A quick rule of thumb: if you need fewer than half the properties, reach for Pick. If you only need to exclude one or two, reach for Omit. Both produce the same kind of derived type — the choice is about readability and how many keys you're working with.

Scenario Preferred utility
Need 2–3 specific fields from a large interface Pick
Need everything except 1–2 sensitive fields Omit
Defining a form input type Pick
Stripping a field before an API response Omit

Conclusion

Pick and Omit are not just convenience shortcuts — they're tools for maintaining a single source of truth for your data shapes. By deriving specialized types from a master interface instead of rewriting them, you eliminate redundancy and let TypeScript enforce consistency automatically. This is DRY applied at the type level, and it becomes increasingly valuable as your interfaces grow and your codebase scales.

Top comments (0)