How to Use Zod's pick and omit for Partial Object Types: A Complete Guide
Zod's pick and omit methods let you derive new object schemas by selecting specific keys or excluding unwanted ones, returning immutable ZodObject instances that preserve type safety.
When building TypeScript applications with the colinhacks/zod library, you frequently need to create variations of existing object schemas for different API endpoints or form validations. The pick and omit utilities provide a type-safe way to construct partial object types by surgically modifying a base schema's shape without mutating the original definition.
Understanding Zod pick and omit
Zod implements pick and omit as core utilities that operate on ZodObject schemas. Both methods accept a mask object where truthy values indicate which keys to select or remove.
The pick Method
The pick method creates a new schema containing only the keys specified in the mask. According to the source code in packages/zod/src/v4/core/util.ts (lines 593-620), the implementation follows four strict steps:
- Validate mask keys – Each key in the mask must exist on the source schema; otherwise Zod throws an "Unrecognized key" error.
- Filter shape – Only keys with truthy mask values are copied into a new shape object.
- Cache result – The new shape is stored via
assignProp(this, "shape", newShape)for future accesses. - Clone schema – Returns a shallow clone using
clone(schema, def)without altering the original.
The omit Method
Conversely, omit builds a new schema containing all keys except those marked truthy in the mask. Found in packages/zod/src/v4/core/util.ts (lines 622-649), this method mirrors pick but copies the full shape first, then deletes each masked key before caching and cloning.
API Styles: Instance Methods vs Static Helpers
Zod exposes these utilities through two equivalent APIs:
Instance methods – Called directly on any ZodObject:
const UserPreview = User.pick({ name: true, email: true });
const UserPublic = User.omit({ password: true, ssn: true });
Static helpers – Convenience wrappers located in packages/zod/src/v4/mini/schemas.ts (lines 911-925 for pick, lines 918-925 for omit):
const UserPreview = z.pick(User, { name: true });
const UserPublic = z.omit(User, { password: true });
Both approaches delegate to the same core implementation in util.ts and produce identical results.
Core Implementation and Safety Guardrails
Refinement Validation
Before processing any keys, both methods check the source schema's refinement list (currDef.checks). If any refinements exist, Zod throws immediately:
if (hasChecks) {
throw new Error(".pick() cannot be used on object schemas containing refinements");
}
This guardrail prevents shape-changing operations from breaking custom validation logic attached to the original schema.
Immutability Guarantees
The implementation ensures the original schema remains untouched by:
- Creating a new shape object rather than mutating the existing one
- Using
clone(schema, def)to produce the returned schema - Preserving all non-shape properties (descriptions, error maps, etc.) on the new instance
Practical Code Examples
import { z } from "zod";
/* ── Base schema ────────────────────────────────────────────── */
const User = z.object({
id: z.string(),
name: z.string(),
email: z.string().email(),
age: z.number().optional(),
password: z.string().min(8),
});
/* ── pick – keep only the listed keys ──────────────────────── */
const UserPreview = User.pick({ name: true, email: true });
/* Type inferred as: { name: string; email: string } */
UserPreview.parse({ name: "Alice", email: "alice@example.com" }); // ✅
// UserPreview.parse({ id: "1" }); // ❌ runtime error – unknown key
/* ── omit – drop sensitive fields ─────────────────────────────── */
const UserPublic = User.omit({ password: true, id: true });
/* Type inferred as: { name: string; email: string; age?: number } */
UserPublic.parse({ name: "Bob", email: "bob@example.com" }); // ✅
// UserPublic.parse({ password: "secret123" }); // ❌ runtime error – `password` removed
/* ── Combining with partial for update schemas ───────────────── */
const UserUpdate = User.pick({ name: true, email: true, age: true }).partial();
/* Type: { name?: string; email?: string; age?: number } */
Summary
- Immutability: Both
pickandomitreturn newZodObjectinstances without modifying the source schema. - Safety checks: Schemas containing refinements cannot use these methods to prevent validation logic corruption.
- Dual API: Use either instance methods (
schema.pick()) or static helpers (z.pick()) based on your coding style. - Type inference: TypeScript automatically narrows the output type to match the selected or remaining keys.
- Test coverage: Reference implementations are validated in
packages/zod/src/v4/classic/tests/pickomit.test.tsandpackages/zod/src/v4/mini/tests/object.test.ts.
Frequently Asked Questions
Can I use pick and omit on schemas that have refinements?
No. According to the source code in packages/zod/src/v4/core/util.ts, both methods check for the presence of refinements (hasChecks) and throw a runtime error if any exist. This prevents you from accidentally breaking custom validation logic that depends on the original object shape.
Do pick and omit modify the original schema object?
No. Zod treats these operations as immutable transformations. The methods create a new shape object, cache it on a cloned schema instance via clone(schema, def), and return the clone while leaving the original schema untouched.
What is the difference between the instance method and static helper versions?
There is no functional difference. The static helpers z.pick(schema, mask) and z.omit(schema, mask) defined in packages/zod/src/v4/mini/schemas.ts are thin wrappers that forward arguments to the instance methods. Choose based on readability—use static helpers when you want to treat the operation as a functional transformation, and instance methods for fluent chaining.
Can I chain pick or omit with other Zod methods like partial or required?
Yes. Because pick and omit return full ZodObject instances, you can chain them with any other object method. For example, User.pick({ name: true, email: true }).partial() creates a schema where both name and email are optional, which is useful for PATCH request validation in REST APIs.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →