How to Use Zod's Mask Method for Partial Field Handling

TLDR: Supply an object mask with truthy values (e.g., { name: true, age: true }) to Zod's partial(), required(), pick(), or omit() methods to selectively transform only those specific fields while leaving the rest of the schema untouched.

The mask argument in Zod enables precise, field-level control over object schema transformations without affecting the entire shape. Implemented in the colinhacks/zod repository, this feature allows you to target specific properties for partial handling while maintaining strict type safety through runtime key validation. Understanding how to use Zod's mask method for partial field handling is essential for building flexible, composable validation schemas that adapt to complex input requirements.

Understanding the Mask Type Definition

At the core of this functionality lies the Mask type defined in packages/zod/src/v4/core/util.ts#L100. This type serves as a simple record where keys represent the fields you want to transform and values must be truthy (typically true) to activate the transformation.

export type Mask<Keys extends PropertyKey> = { [K in Keys]?: true };

Any falsy value assigned to a key is ignored during processing, which allows you to construct masks programmatically without pre-filtering your configuration objects.

Internal Implementation of Partial with Masks

The partial utility function in packages/zod/src/v4/core/util.ts#L709-L756 handles the mask logic internally. When you provide a mask object, the function iterates over each key, validates its existence in the original schema shape, and—only if the value is truthy—wraps that property in a ZodOptional schema.

export function partial(
  Class: SchemaClass<schemas.$ZodOptional> | null,
  schema: schemas.$ZodObject,
  mask: object | undefined
): any {
  // ... schema processing logic ...
  if (mask) {
    for (const key in mask) {
      if (!(key in oldShape)) {
        throw new Error(`Unrecognized key: "${key}"`);
      }
      if (!(mask as any)[key]) continue;           // falsy → ignore
      shape[key] = Class
        ? new Class({ type: "optional", innerType: oldShape[key]! })
        : oldShape[key]!;
    }
  } else {
    // no mask → every key becomes optional
    for (const key in oldShape) { /* ... */ }
  }
  // ...
}

If you omit the mask argument entirely, the function defaults to making every property in the object optional.

Public API Wiring in ZodObject

The public-facing partial() method on ZodObject instances delegates to this utility function. In packages/zod/src/v4/classic/schemas.ts#L82-L84, the method signature forwards your mask argument directly to the core implementation:

inst.partial = (...args: any[]) => util.partial(ZodOptional, inst, args[0] as object);

The same pattern applies to required(), pick(), and omit(), all of which accept an optional mask argument for selective field handling.

Practical Examples of Using Masks

Selective Partial Conversion

Instead of making every field optional, use a mask to target specific properties. This example from the test suite demonstrates making only age, field, and name optional while keeping country required:

import { z } from "zod";

const user = z.object({
  name: z.string(),
  age: z.number().optional(),
  field: z.string().optional().default("asdf"),
  country: z.string(),
});

const masked = user
  .partial({ age: true, field: true, name: true })
  .strict();

// masked.shape.name is now ZodOptional<ZodString>
// masked.shape.country remains ZodString (required)

masked.parse({ country: "US" }); // ✅ passes

Source: packages/zod/src/v4/classic/tests/partial.test.ts#L10-L27

Ignoring Falsy Values in Masks

The mask implementation skips falsy values, allowing you to conditionally include fields without breaking the schema construction:

const shouldMakeOptional = false;

const partialIgnore = user.partial({ 
  name: true, 
  country: shouldMakeOptional // false → ignored
}).strict();

// Only 'name' becomes optional; 'country' stays required
partialIgnore.shape.country instanceof z.ZodString; // true

Source: packages/zod/src/v4/classic/tests/partial.test.ts#L29-L44

Converting Optional Fields to Required

You can use the mask argument with the required() method to make specific optional fields mandatory again:

const reqMask = user.required({ age: true }).strict();

// age is now required (ZodNonOptional)
// field retains its default value behavior
reqMask.shape.age instanceof z.ZodNonOptional; // true

Source: packages/zod/src/v4/classic/tests/partial.test.ts#L79-L92

Picking and Omitting Fields with Masks

The mask pattern extends to schema filtering. Use pick() to retain only specified fields or omit() to remove specific properties:

// Retain only name and age
const picked = user.pick({ name: true, age: true });
// picked.shape contains only name and age

// Remove the field property
const omitted = user.omit({ field: true });
// omitted.shape excludes field but keeps name, age, and country

Source: packages/zod/src/v4/core/util.ts#L593-L648

Summary

  • Supply a mask object with true values to partial(), required(), pick(), or omit() for selective field transformation instead of schema-wide changes.
  • Runtime validation occurs in util.partial, which throws an Unrecognized key error if you attempt to mask a property that doesn't exist in the original shape.
  • Falsy values are ignored during mask processing, enabling dynamic mask construction where conditions can disable certain fields without error.
  • Consistent API pattern across object methods allows you to compose complex schema transformations while maintaining type safety and clear intent.

Frequently Asked Questions

What happens if I include a non-existent key in the mask?

Zod throws an Unrecognized key error during schema construction. The util.partial function explicitly checks if (!(key in oldShape)) before processing each mask key to prevent typos and ensure type safety.

Can I use masks with methods other than partial()?

Yes, the mask argument works with required(), pick(), and omit() methods on ZodObject instances. All three methods follow the same implementation pattern in packages/zod/src/v4/core/util.ts, accepting a Mask type to target specific fields while preserving the rest of the schema.

Why does Zod ignore falsy values in the mask?

The implementation includes the check if (!(mask as any)[key]) continue to skip falsy entries. This design allows you to build masks programmatically using boolean variables or conditions without needing to filter out false values beforehand, making dynamic schema construction cleaner and more intuitive.

How does partial() with a mask differ from calling partial() without arguments?

Calling partial() without arguments makes every field in the object optional by iterating over oldShape directly. Providing a mask makes only the specified fields optional, leaving all other properties with their original required or optional status intact.

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:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →