Zod Deep Partial Utility

1.4.4 · active · verified Sun Apr 19

zod-deep-partial is a lightweight utility package designed to recursively make all properties within a given Zod schema optional. The package is currently at version 1.4.4 and is actively maintained, indicated by recent updates to support Zod v4 and ongoing development on its GitHub repository. Its primary differentiator from Zod's native `.partial()` method is its deep, recursive application of optionality, making it ideal for scenarios like patch updates or handling partially complete data structures. It boasts comprehensive support for a wide array of Zod types, including nested objects, arrays, unions, discriminated unions, intersections, and various transformations, all while maintaining Zod's robust type inference. Crucially, `zod-deep-partial` maintains a minimal footprint by having zero direct dependencies, relying solely on `zod` as a peer dependency.

Common errors

Warnings

Install

Imports

Quickstart

This quickstart defines a complex Zod schema including primitives, objects, arrays, records, and dates. It then demonstrates how `zodDeepPartial` recursively applies optionality to all properties, showcasing various valid partial inputs and verifying that type inference is correctly preserved, while also illustrating how underlying type validations (like email format) still apply if the property is provided.

import { z } from "zod";
import { zodDeepPartial } from "zod-deep-partial";

// 1. Define your base schema
const userSchema = z.object({
  name: z.string().min(1, "Name is required"),
  email: z.string().email("Invalid email format"),
  profile: z.object({
    bio: z.string().max(200, "Bio too long"),
    avatar: z.string().url("Invalid URL format").nullable().optional(),
  }),
  tags: z.array(z.string().min(1, "Tag cannot be empty")),
  settings: z.record(z.string(), z.boolean()),
  createdAt: z.date(),
});

// 2. Create the deep partial schema
const partialUserSchema = zodDeepPartial(userSchema);

// 3. Use the partial schema for validation and observe type inference

// All of these are now valid:
try {
  partialUserSchema.parse({});
  console.log("Empty object valid.");
} catch (error) {
  console.error("Validation failed for empty object:", error);
}

try {
  partialUserSchema.parse({ name: "John Doe" });
  console.log("Object with name valid.");
} catch (error) {
  console.error("Validation failed for object with name:", error);
}

try {
  partialUserSchema.parse({ profile: {} });
  console.log("Object with empty profile valid.");
} catch (error) {
  console.error("Validation failed for object with empty profile:", error);
}

try {
  partialUserSchema.parse({ profile: { bio: "A developer" } });
  console.log("Object with partial profile valid.");
} catch (error) {
  console.error("Validation failed for object with partial profile:", error);
}

try {
  partialUserSchema.parse({ tags: ["developer"] });
  console.log("Object with tags valid.");
} catch (error) {
  console.error("Validation failed for object with tags:", error);
}

// Example of what still fails (underlying type validations still apply if present)
try {
  partialUserSchema.parse({ email: "invalid-email" }); // Should fail because 'email' is present but malformed
  console.log("This should not be valid but passed (check expected behavior).");
} catch (error: any) {
  console.log("Expected validation failure for invalid email:", error.issues[0].message);
}

// Type inference is preserved correctly
type PartialUser = z.infer<typeof partialUserSchema>;
// Expected type:
/*
{
    name?: string | undefined;
    email?: string | undefined;
    profile?: {
        bio?: string | undefined;
        avatar?: string | null | undefined;
    } | undefined;
    tags?: (string | undefined)[] | undefined;
    settings?: Record<string, boolean> | undefined;
    createdAt?: Date | undefined;
}
*/
console.log("Type inference for PartialUser looks correct in IDE.");

view raw JSON →