Skip to content
JL

Home

About

Blog

Contact

Shop

Portfolio

Privacy

TOS

Click to navigate

  1. Home
  2. Joshua R. Lehman's Blog
  3. Utility Types Part 3: Pick and Omit

Table of contents

  • Share on X
  • Discuss on X

Related Articles

Utility Types Part 2: Readonly and Record
TypeScript
10m
Aug 23, 2026

Utility Types Part 2: Readonly and Record

Two utility types that occupy opposite ends of the structural transformation space: Readonly locks an object's properties against mutation at the type level, while Record builds typed dictionary structures from a set of keys and a value type. Neither modifies optionality — they operate on shape and mutability instead.

#Utility Types#Readonly+5
Utility Types Part 1: Partial and Required
TypeScript
9m
Aug 16, 2026

Utility Types Part 1: Partial and Required

TypeScript ships with a set of built-in generic types — called utility types — that perform common type transformations on existing types. Rather than manually rewriting every property of an object type, you pass your type to a utility type and receive a transformed version. Partial and Required are the two complementary utility types that control whether every property in an object is optional or required.

#Utility Types#Partial+5
Utility Types Part 5: ReturnType and Parameters
TypeScript
9m
Sep 13, 2026

Utility Types Part 5: ReturnType and Parameters

ReturnType and Parameters are utility types that inspect function types rather than object types or unions. ReturnType extracts the type a function returns. Parameters extracts the types of its arguments as a tuple. Both are implemented using conditional types with infer, and both are essential for writing type-safe wrappers, decorators, and higher-order functions without duplicating type information.

#Utility Types#ReturnType+5
Ask me anything! 💬

© Joshua R. Lehman

Full Stack Developer

Crafted with passion • Built with modern web technologies

2026 • All rights reserved

Contents

  • What Pick Does
  • How Pick Is Implemented
  • What Omit Does
  • How Omit Is Implemented
  • Practical Patterns for Pick
  • Practical Patterns for Omit
  • Combining Pick and Omit
  • What Is Next
  • Key Takeaways
TypeScript

Utility Types Part 3: Pick and Omit

August 30, 2026•7 min read
Joshua R. Lehman
Joshua R. Lehman
Author
TypeScript Pick and Omit utility types selecting and excluding properties from object types
Utility Types Part 3: Pick and Omit

The utility types covered so far — Partial, Required, Readonly, and Record — each transform all properties of a type uniformly. Pick and Omit work differently: they restructure a type by selecting which properties to include or exclude by name. The result is a new type that shares structure with the source type but contains only the properties you specify. This is the primary mechanism for deriving focused, minimal types from larger base types without repeating property definitions.

What Pick Does

Pick<T, K> takes a type T and a key or union of keys K, and returns a new type containing only the properties of T whose names are in K.

type User = {
  id: number;
  name: string;
  email: string;
  passwordHash: string;
  createdAt: Date;
  role: "admin" | "user";
};
 
type UserPreview = Pick<User, "id" | "name">;
// {
//   id: number;
//   name: string;
// }
 
type UserProfile = Pick<User, "id" | "name" | "email" | "role">;
// {
//   id: number;
//   name: string;
//   email: string;
//   role: "admin" | "user";
// }

Pick preserves the exact types of each property — id is still number, role is still "admin" | "user". Only the set of included properties changes. Properties not named in K simply do not appear in the result.

The key constraint is enforced: if K includes a property name that does not exist in T, TypeScript produces an error. Pick<User, "nonexistent"> is a type error.

How Pick Is Implemented

Pick<T, K> is defined as:

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

The constraint K extends keyof T ensures that every key in K is an actual property of T. The mapped type then iterates over K — not over keyof T — and uses indexed access T[P] to retrieve the original property type. Properties of T not in K are simply not iterated and therefore not included in the result.

Pick Preserves Optional and Readonly Modifiers

Pick preserves the modifiers of the original properties. If a property in T is optional (?) or readonly (readonly), the picked version of that property retains those modifiers. Pick<T, K> does not strip optionality or add it — it is a structural subset, not a transformation. To combine picking with modifier changes, compose Pick with Partial or Readonly: Partial<Pick<T, K>> produces a type with the named properties all made optional.

What Omit Does

Omit<T, K> is the complement of Pick. It takes a type T and a key or union of keys K, and returns a new type containing every property of T except those named in K.

type User = {
  id: number;
  name: string;
  email: string;
  passwordHash: string;
  createdAt: Date;
  role: "admin" | "user";
};
 
type PublicUser = Omit<User, "passwordHash">;
// {
//   id: number;
//   name: string;
//   email: string;
//   createdAt: Date;
//   role: "admin" | "user";
// }
 
type NewUser = Omit<User, "id" | "createdAt">;
// {
//   name: string;
//   email: string;
//   passwordHash: string;
//   role: "admin" | "user";
// }

Omit is useful when you have a large type and only need to remove a few fields. It is the natural inverse of Pick — use Pick when you know which properties to include; use Omit when you know which properties to exclude.

Omit Does Not Enforce That Excluded Keys Exist

Unlike Pick, which constrains K extends keyof T and errors on unknown keys, Omit's key parameter accepts any string | number | symbol. In TypeScript's standard library definition, Omit<T, K extends string | number | symbol> does not require K to be a key of T. Omitting a property name that does not exist in T silently produces T unchanged — no error. This can mask typos in property names. Some teams address this by using a stricter custom StrictOmit that constrains K extends keyof T.

How Omit Is Implemented

Omit<T, K> is defined as:

type Omit<T, K extends string | number | symbol> = Pick<T, Exclude<keyof T, K>>;

Omit is implemented in terms of Pick and Exclude. Exclude<keyof T, K> produces the union of all keys in T that are not in K. Pick<T, ...> then selects those remaining keys. The full implementation depends on Exclude, which is covered in the next post — for now, understanding that Omit<T, K> = Pick<T, keys-of-T-not-in-K> is sufficient.

Practical Patterns for Pick

View models and projections. When rendering UI components, a component often needs only a subset of a data model's properties. Pick creates a focused type that documents exactly what the component requires:

type Product = {
  id: number;
  name: string;
  description: string;
  price: number;
  stockCount: number;
  supplierId: number;
  createdAt: Date;
};
 
type ProductCard = Pick<Product, "id" | "name" | "price">;
 
function renderProductCard(product: ProductCard): string {
  return `${product.name} — $${product.price}`;
}

The ProductCard type communicates the component's contract precisely: it accepts any object with those three properties, regardless of whether the full Product type or some other type with the same fields is passed.

API response shapes. Different API endpoints return different subsets of a model. Pick names those subsets explicitly:

type UserListItem = Pick<User, "id" | "name" | "role">;
type UserDetailView = Pick<
  User,
  "id" | "name" | "email" | "role" | "createdAt"
>;

Type-safe property selection. Generic functions that accept an array of keys and return a projection can use Pick to type the result:

function pickFields<T, K extends keyof T>(obj: T, keys: K[]): Pick<T, K> {
  const result = {} as Pick<T, K>;
  for (const key of keys) {
    result[key] = obj[key];
  }
  return result;
}
 
const user: User = {
  id: 1,
  name: "Alice",
  email: "[email protected]",
  passwordHash: "...",
  createdAt: new Date(),
  role: "user",
};
const preview = pickFields(user, ["id", "name"]);
// preview: Pick<User, "id" | "name"> = { id: number; name: string }

Practical Patterns for Omit

Excluding sensitive fields. The most common use of Omit is removing properties that should not be exposed — passwords, internal identifiers, audit fields:

type SafeUser = Omit<User, "passwordHash">;
 
function getUserProfile(id: number): Promise<SafeUser> {
  // Guaranteed not to include passwordHash in the return type
}

Create DTOs. When inserting a new record, the database generates certain fields (id, createdAt). The input type for creation omits those:

type CreateUserInput = Omit<User, "id" | "createdAt">;
// {
//   name: string;
//   email: string;
//   passwordHash: string;
//   role: "admin" | "user";
// }
 
function createUser(input: CreateUserInput): Promise<User> {
  // ...
}

Update DTOs. Combined with Partial from the previous post, Omit produces the standard update payload type:

type UpdateUserInput = Partial<Omit<User, "id" | "createdAt" | "passwordHash">>;
// {
//   name?: string;
//   email?: string;
//   role?: "admin" | "user";
// }

Omit for Derived Types That Stay in Sync

When you define a CreateInput or UpdateInput type using Omit<BaseType, ...>, it automatically stays synchronised with the base type. If you add a field to User, CreateUserInput automatically includes it (unless you explicitly omit it). This is the key advantage over writing each derived type manually — changes to the source type propagate automatically rather than requiring parallel updates across multiple type definitions.

Combining Pick and Omit

Pick and Omit are often combined with other utility types or with each other to express complex transformations in a single, readable expression.

Combining Omit with Partial is covered above. Combining Pick with Readonly produces an immutable projection:

type ReadonlyUserPreview = Readonly<Pick<User, "id" | "name">>;
// { readonly id: number; readonly name: string }

Composing multiple Omit calls is sometimes more readable as a single Pick:

// These are equivalent when User has exactly these six properties:
type PublicUser1 = Omit<User, "passwordHash" | "createdAt">;
type PublicUser2 = Pick<User, "id" | "name" | "email" | "role">;

Choose Pick when the list of included properties is shorter; choose Omit when the list of excluded properties is shorter. For large types where only one or two fields need to be removed, Omit is almost always the right choice.

Pick and Omit for Interface Segregation

The interface segregation principle states that no code should depend on methods or properties it does not use. Pick implements this principle at the type level — components receive exactly the properties they need, not the full model. This makes dependencies explicit, reduces unintended coupling, and makes types easier to test and reuse. A function typed with Pick<User, "id" | "name"> works with any object that has those two properties, not only with full User objects.

What Is Next

Pick and Omit complete the structural subset utility types. The next post covers Exclude and Extract — two utility types that operate on union types rather than object types. Where Pick and Omit select properties from an object type, Exclude and Extract select members from a union type. Understanding both pairs gives you precise control over both object shapes and union compositions.

Key Takeaways

  • Pick<T, K> creates a type with only the named properties of T; the constraint K extends keyof T ensures all named keys exist
  • Omit<T, K> creates a type with all properties of T except the named ones; its key parameter is not constrained to actual keys of T, so typos produce no error
  • Both utility types preserve the optionality and readonly modifiers of the original properties — they do not transform modifiers, only property membership
  • Pick is implemented as a mapped type over K; Omit is implemented as Pick<T, Exclude<keyof T, K>>
  • Use Pick when specifying the included properties is shorter; use Omit when specifying the excluded properties is shorter
  • Compose Omit with Partial for update DTOs, with Readonly for immutable projections, and with other utility types to build precise derived types that stay synchronised with their source