Primitive Types

  1. Introduction



  2. z.string()

  3. const schema = z.string();
    
    schema.parse("hello");      // ✅ Returns "hello"
    schema.parse("");           // ✅ Returns ""
    schema.parse(123);          // ❌ Throws ZodError: Expected string, received number
    schema.parse(null);         // ❌ Throws ZodError: Expected string, received null
    


  4. z.number()

  5. const schema = z.number();
    
    schema.parse(42);           // ✅ Returns 42
    schema.parse(3.14);         // ✅ Returns 3.14
    schema.parse(-100);         // ✅ Returns -100
    schema.parse(Infinity);     // ✅ Returns Infinity
    schema.parse("42");         // ❌ Throws ZodError: Expected number, received string
    schema.parse(NaN);          // ❌ Throws ZodError: Expected number, received nan
    


  6. z.boolean()

  7. const schema = z.boolean();
    
    schema.parse(true);         // ✅ Returns true
    schema.parse(false);        // ✅ Returns false
    schema.parse(1);            // ❌ Throws ZodError: Expected boolean, received number
    schema.parse("true");       // ❌ Throws ZodError: Expected boolean, received string
    


  8. z.bigint()

  9. const schema = z.bigint();
    
    schema.parse(10n);          // ✅ Returns 10n
    schema.parse(BigInt(999));  // ✅ Returns 999n
    schema.parse(10);           // ❌ Throws ZodError: Expected bigint, received number
    schema.parse("10");         // ❌ Throws ZodError: Expected bigint, received string
    


  10. z.date()

  11. const schema = z.date();
    
    schema.parse(new Date());                    // ✅ Returns Date object
    schema.parse(new Date("2024-01-15"));        // ✅ Returns Date object
    schema.parse(new Date("invalid"));           // ❌ Throws ZodError: Invalid date
    schema.parse("2024-01-15");                  // ❌ Throws ZodError: Expected date, received string
    schema.parse(1705276800000);                 // ❌ Throws ZodError: Expected date, received number
    


  12. z.undefined()

  13. const schema = z.undefined();
    
    schema.parse(undefined);    // ✅ Returns undefined
    schema.parse(void 0);       // ✅ Returns undefined
    schema.parse(null);         // ❌ Throws ZodError: Expected undefined, received null
    schema.parse("");           // ❌ Throws ZodError: Expected undefined, received string
    


  14. z.null()

  15. const schema = z.null();
    
    schema.parse(null);         // ✅ Returns null
    schema.parse(undefined);    // ❌ Throws ZodError: Expected null, received undefined
    schema.parse(0);            // ❌ Throws ZodError: Expected null, received number
    


  16. z.any()

  17. const schema = z.any();
    
    schema.parse("hello");      // ✅ Returns "hello"
    schema.parse(123);          // ✅ Returns 123
    schema.parse(null);         // ✅ Returns null
    schema.parse(undefined);    // ✅ Returns undefined
    schema.parse({ a: 1 });     // ✅ Returns { a: 1 }
    


  18. z.unknown()

  19. const schema = z.unknown();
    
    schema.parse("hello");      // ✅ Returns "hello"
    schema.parse(123);          // ✅ Returns 123
    schema.parse(null);         // ✅ Returns null
    
    // TypeScript behavior:
    const result = schema.parse(someData);
    result.toUpperCase();       // ❌ TypeScript error: 'result' is of type 'unknown'
    
    // Must narrow first:
    if (typeof result === "string") {
        result.toUpperCase();   // ✅ OK
    }
    


  20. z.never()

  21. const schema = z.never();
    
    schema.parse("anything");   // ❌ Throws ZodError
    schema.parse(undefined);    // ❌ Throws ZodError
    schema.parse(null);         // ❌ Throws ZodError
    
    // Practical use: exhaustive switch
    type Status = "active" | "inactive";
    function handleStatus(status: Status) {
        switch (status) {
            case "active": return "on";
            case "inactive": return "off";
            default:
                z.never().parse(status); // Compile-time + runtime check for unhandled cases
        }
    }
    


  22. z.void()

  23. const schema = z.void();
    
    schema.parse(undefined);    // ✅ Returns undefined
    schema.parse(null);         // ❌ Throws ZodError: Expected void, received null
    schema.parse("hello");      // ❌ Throws ZodError: Expected void, received string
    
    // Typical use: function return type
    const fnSchema = z.function().args(z.string()).returns(z.void());
    



String Validators

  1. Introduction



  2. .min()

  3. const schema = z.string().min(3);
    
    schema.parse("hello");   // ✅ Returns "hello"
    schema.parse("abc");     // ✅ Returns "abc"
    schema.parse("ab");      // ❌ ZodError: String must contain at least 3 character(s)
    schema.parse("");        // ❌ ZodError
    
    // Custom message
    z.string().min(3, "Too short").parse("ab");  // ❌ ZodError: Too short
    


  4. .max()

  5. const schema = z.string().max(5);
    
    schema.parse("hi");      // ✅ Returns "hi"
    schema.parse("hello");   // ✅ Returns "hello"
    schema.parse("hello!");  // ❌ ZodError: String must contain at most 5 character(s)
    


  6. .length()

  7. const schema = z.string().length(4);
    
    schema.parse("abcd");    // ✅ Returns "abcd"
    schema.parse("abc");     // ❌ ZodError: String must contain exactly 4 character(s)
    schema.parse("abcde");   // ❌ ZodError
    


  8. .email()

  9. const schema = z.string().email();
    
    schema.parse("user@example.com");    // ✅ Returns "user@example.com"
    schema.parse("test@sub.domain.org"); // ✅ Returns "test@sub.domain.org"
    schema.parse("invalid");             // ❌ ZodError: Invalid email
    schema.parse("user@");               // ❌ ZodError: Invalid email
    schema.parse("@example.com");        // ❌ ZodError: Invalid email
    


  10. .url()

  11. const schema = z.string().url();
    
    schema.parse("https://example.com");       // ✅
    schema.parse("http://localhost:3000");     // ✅
    schema.parse("ftp://files.example.com");   // ✅
    schema.parse("example.com");               // ❌ ZodError: Invalid url
    schema.parse("not a url");                 // ❌ ZodError: Invalid url
    


  12. .uuid()

  13. const schema = z.string().uuid();
    
    schema.parse("550e8400-e29b-41d4-a716-446655440000");  // ✅
    schema.parse("123e4567-e89b-12d3-a456-426614174000");  // ✅
    schema.parse("not-a-uuid");                            // ❌ ZodError: Invalid uuid
    schema.parse("550e8400-e29b-41d4-a716");               // ❌ ZodError: Invalid uuid
    


  14. .regex()

  15. const schema = z.string().regex(/^[A-Z]{3}-\d{3}$/);
    
    schema.parse("ABC-123");   // ✅ Returns "ABC-123"
    schema.parse("XYZ-999");   // ✅ Returns "XYZ-999"
    schema.parse("abc-123");   // ❌ ZodError: Invalid
    schema.parse("ABCD-12");   // ❌ ZodError: Invalid
    
    // Custom message
    z.string().regex(/^\d+$/, "Numbers only").parse("abc");  // ❌ ZodError: Numbers only
    


  16. .startsWith()

  17. const schema = z.string().startsWith("https://");
    
    schema.parse("https://example.com");  // ✅
    schema.parse("https://");             // ✅
    schema.parse("http://example.com");   // ❌ ZodError: Invalid input: must start with "https://"
    


  18. .endsWith()

  19. const schema = z.string().endsWith(".json");
    
    schema.parse("config.json");    // ✅
    schema.parse("data.json");      // ✅
    schema.parse("config.yaml");    // ❌ ZodError: Invalid input: must end with ".json"
    


  20. .trim()

  21. const schema = z.string().trim();
    
    schema.parse("  hello  ");   // ✅ Returns "hello"
    schema.parse("\n\tworld\n"); // ✅ Returns "world"
    schema.parse("no spaces");   // ✅ Returns "no spaces"
    
    // Often combined with .min() to reject empty/whitespace-only
    const nonEmpty = z.string().trim().min(1);
    nonEmpty.parse("   ");       // ❌ ZodError: String must contain at least 1 character(s)
    


  22. .toLowerCase()

  23. const schema = z.string().toLowerCase();
    
    schema.parse("HELLO");       // ✅ Returns "hello"
    schema.parse("HeLLo WoRLD"); // ✅ Returns "hello world"
    schema.parse("already");     // ✅ Returns "already"
    


  24. .toUpperCase()

  25. const schema = z.string().toUpperCase();
    
    schema.parse("hello");       // ✅ Returns "HELLO"
    schema.parse("HeLLo WoRLD"); // ✅ Returns "HELLO WORLD"
    schema.parse("ALREADY");     // ✅ Returns "ALREADY"
    


  26. Chaining Example

  27. const usernameSchema = z.string()
        .trim()
        .toLowerCase()
        .min(3)
        .max(20)
        .regex(/^[a-z0-9_]+$/);
    
    usernameSchema.parse("  John_Doe123  ");  // ✅ Returns "john_doe123"
    usernameSchema.parse("  AB  ");           // ❌ ZodError: min 3
    usernameSchema.parse("invalid-name!");    // ❌ ZodError: regex fails
    



Number Validators

  1. Introduction



  2. .min()

  3. const schema = z.number().min(5);
    
    schema.parse(5);     // ✅ Returns 5
    schema.parse(10);    // ✅ Returns 10
    schema.parse(4.99);  // ❌ ZodError: Number must be greater than or equal to 5
    schema.parse(-10);   // ❌ ZodError
    


  4. .max()

  5. const schema = z.number().max(100);
    
    schema.parse(100);   // ✅ Returns 100
    schema.parse(50);    // ✅ Returns 50
    schema.parse(101);   // ❌ ZodError: Number must be less than or equal to 100
    


  6. .int()

  7. const schema = z.number().int();
    
    schema.parse(42);    // ✅ Returns 42
    schema.parse(-10);   // ✅ Returns -10
    schema.parse(0);     // ✅ Returns 0
    schema.parse(3.14);  // ❌ ZodError: Expected integer, received float
    schema.parse(5.0);   // ✅ Returns 5 (5.0 === 5 in JS)
    


  8. .positive()

  9. const schema = z.number().positive();
    
    schema.parse(1);     // ✅ Returns 1
    schema.parse(0.001); // ✅ Returns 0.001
    schema.parse(0);     // ❌ ZodError: Number must be greater than 0
    schema.parse(-5);    // ❌ ZodError
    


  10. .negative()

  11. const schema = z.number().negative();
    
    schema.parse(-1);    // ✅ Returns -1
    schema.parse(-0.01); // ✅ Returns -0.01
    schema.parse(0);     // ❌ ZodError: Number must be less than 0
    schema.parse(5);     // ❌ ZodError
    


  12. .nonnegative()

  13. const schema = z.number().nonnegative();
    
    schema.parse(0);     // ✅ Returns 0
    schema.parse(100);   // ✅ Returns 100
    schema.parse(-1);    // ❌ ZodError: Number must be greater than or equal to 0
    schema.parse(-0.01); // ❌ ZodError
    



  14. .finite()

  15. const schema = z.number().finite();
    
    schema.parse(999999999);   // ✅ Returns 999999999
    schema.parse(-999999999);  // ✅ Returns -999999999
    schema.parse(0);           // ✅ Returns 0
    schema.parse(Infinity);    // ❌ ZodError: Number must be finite
    schema.parse(-Infinity);   // ❌ ZodError
    


  16. .safe()

  17. const schema = z.number().safe();
    
    schema.parse(9007199254740991);   // ✅ MAX_SAFE_INTEGER
    schema.parse(-9007199254740991);  // ✅ MIN_SAFE_INTEGER
    schema.parse(9007199254740992);   // ❌ ZodError: Number must be safe
    
    // Why this matters:
    console.log(9007199254740992 === 9007199254740993);  // true (precision lost!)
    


  18. Additional Number Validators

  19. Method Signature Description
    .gt(n) ZodNumber.gt(value: number, message?): ZodNumber Greater than (exclusive)
    .gte(n) ZodNumber.gte(value: number, message?): ZodNumber Alias for .min()
    .lt(n) ZodNumber.lt(value: number, message?): ZodNumber Less than (exclusive)
    .lte(n) ZodNumber.lte(value: number, message?): ZodNumber Alias for .max()
    .multipleOf(n) ZodNumber.multipleOf(value: number, message?): ZodNumber Must be divisible by n


  20. Chaining Example
  21. const ageSchema = z.number().int().nonnegative().max(120);
    
    ageSchema.parse(25);    // ✅ Returns 25
    ageSchema.parse(25.5);  // ❌ Not an integer
    ageSchema.parse(-1);    // ❌ Negative
    ageSchema.parse(150);   // ❌ Exceeds max
    



Composite Types

  1. Introduction



  2. z.object()

  3. const UserSchema = z.object({
        name: z.string(),
        age: z.number(),
    });
    
    UserSchema.parse({ name: "Alice", age: 30 });
    // ✅ Returns { name: "Alice", age: 30 }
    
    UserSchema.parse({ name: "Bob", age: 25, extra: "ignored" });
    // ✅ Returns { name: "Bob", age: 25 } — extra key stripped
    
    UserSchema.parse({ name: "Charlie" });
    // ❌ ZodError: age is required
    
    UserSchema.parse({ name: "Dave", age: "thirty" });
    // ❌ ZodError: age expected number, received string
    
    // Type inference
    type User = z.infer<typeof UserSchema>;
    // { name: string; age: number }
    


  4. z.array()

  5. const schema = z.array(z.number());
    
    schema.parse([1, 2, 3]);       // ✅ Returns [1, 2, 3]
    schema.parse([]);              // ✅ Returns []
    schema.parse([1, "two", 3]);   // ❌ ZodError: Expected number at index 1
    schema.parse("not array");     // ❌ ZodError: Expected array
    
    // Array-specific validators
    z.array(z.string()).min(1);          // At least 1 element
    z.array(z.string()).max(5);          // At most 5 elements
    z.array(z.string()).length(3);       // Exactly 3 elements
    z.array(z.string()).nonempty();      // Alias for .min(1), also narrows type
    


  6. z.tuple()

  7. z.tuple<T extends [ZodTypeAny, ...ZodTypeAny[]]>(items: T): ZodTuple<T>
    
    // With rest element
    z.tuple<T, R extends ZodTypeAny>(items: T).rest(rest: R): ZodTuple<T, R>
    

    const schema = z.tuple([z.string(), z.number(), z.boolean()]);
    
    schema.parse(["hello", 42, true]);   // ✅ Returns ["hello", 42, true]
    schema.parse(["hi", 1, false]);      // ✅
    schema.parse(["hello", 42]);         // ❌ ZodError: Expected 3 items, received 2
    schema.parse([42, "hello", true]);   // ❌ ZodError: Type mismatch at index 0
    
    // With rest element
    const withRest = z.tuple([z.string(), z.number()]).rest(z.boolean());
    withRest.parse(["a", 1]);                    // ✅
    withRest.parse(["a", 1, true, false, true]); // ✅
    withRest.parse(["a", 1, "nope"]);            // ❌ Rest element must be boolean
    


  8. z.record()

  9. // Value-only (keys are strings)
    z.record<V extends ZodTypeAny>(valueSchema: V): ZodRecord<ZodString, V>
    
    // Key and value
    z.record<K extends ZodString | ZodEnum | ZodUnion, V extends ZodTypeAny>(
        keySchema: K,
        valueSchema: V
    ): ZodRecord<K, V>
    

    // String keys, number values
    const scores = z.record(z.number());
    
    scores.parse({ alice: 100, bob: 85 });  // ✅
    scores.parse({});                        // ✅
    scores.parse({ alice: "high" });         // ❌ Value must be number
    
    // Constrained keys with enum
    const StatusMap = z.record(
        z.enum(["pending", "active", "done"]),
        z.boolean()
    );
    
    StatusMap.parse({ pending: true, active: false });  // ✅
    StatusMap.parse({ invalid: true });                 // ❌ Invalid key
    


  10. z.map()

  11. const schema = z.map(z.string(), z.number());
    
    const validMap = new Map([["a", 1], ["b", 2]]);
    const invalidMap = new Map([["a", "one"]]);
    
    schema.parse(validMap);     // ✅ Returns Map { "a" => 1, "b" => 2 }
    schema.parse(invalidMap);   // ❌ ZodError: Value must be number
    schema.parse({});           // ❌ ZodError: Expected Map
    schema.parse(new Map());    // ✅ Empty map is valid
    


  12. z.set()

  13. const schema = z.set(z.number());
    
    schema.parse(new Set([1, 2, 3]));      // ✅ Returns Set { 1, 2, 3 }
    schema.parse(new Set());               // ✅ Empty set
    schema.parse(new Set([1, "two"]));     // ❌ ZodError: Expected number
    schema.parse([1, 2, 3]);               // ❌ ZodError: Expected Set
    
    // Size constraints
    z.set(z.string()).min(1);    // At least 1 element
    z.set(z.string()).max(10);   // At most 10 elements
    z.set(z.string()).size(5);   // Exactly 5 elements
    


  14. z.enum()

  15. const Status = z.enum(["pending", "active", "completed"]);
    
    Status.parse("active");      // ✅ Returns "active"
    Status.parse("pending");     // ✅
    Status.parse("invalid");     // ❌ ZodError: Invalid enum value
    Status.parse(123);           // ❌ ZodError: Expected string
    
    // Access enum values
    Status.options;              // ["pending", "active", "completed"]
    Status.enum.active;          // "active" (for autocomplete)
    
    // Type inference
    type Status = z.infer<typeof Status>;  // "pending" | "active" | "completed"
    


  16. z.nativeEnum()

  17. enum Direction {
        Up = "UP",
        Down = "DOWN",
        Left = "LEFT",
        Right = "RIGHT",
    }
    
    const schema = z.nativeEnum(Direction);
    
    schema.parse("UP");           // ✅ Returns "UP"
    schema.parse(Direction.Up);   // ✅ Returns "UP"
    schema.parse("DIAGONAL");     // ❌ ZodError: Invalid enum value
    
    // Numeric enums
    enum StatusCode {
        OK = 200,
        NotFound = 404,
    }
    
    const codeSchema = z.nativeEnum(StatusCode);
    codeSchema.parse(200);        // ✅
    codeSchema.parse(500);        // ❌
    


  18. z.union()

  19. const schema = z.union([z.string(), z.number()]);
    
    schema.parse("hello");   // ✅ Returns "hello"
    schema.parse(42);        // ✅ Returns 42
    schema.parse(true);      // ❌ ZodError: Invalid input
    
    // Shorthand with .or()
    const same = z.string().or(z.number());
    
    // Complex union
    const Response = z.union([
        z.object({ success: z.literal(true), data: z.string() }),
        z.object({ success: z.literal(false), error: z.string() }),
    ]);
    


  20. z.discriminatedUnion()

  21. z.discriminatedUnion<
        Discriminator extends string,
        Types extends [ZodObject<any>, ...ZodObject<any>[]]
    >(discriminator: Discriminator, types: Types): ZodDiscriminatedUnion<Discriminator, Types>
    

    const Result = z.discriminatedUnion("status", [
        z.object({ status: z.literal("success"), data: z.string() }),
        z.object({ status: z.literal("error"), message: z.string() }),
    ]);
    
    Result.parse({ status: "success", data: "hello" });
    // ✅ Returns { status: "success", data: "hello" }
    
    Result.parse({ status: "error", message: "failed" });
    // ✅
    
    Result.parse({ status: "error", data: "wrong field" });
    // ❌ ZodError: message is required (knows which branch based on "status")
    
    Result.parse({ status: "unknown" });
    // ❌ ZodError: Invalid discriminator value
    


  22. z.intersection()

  23. const A = z.object({ a: z.string() });
    const B = z.object({ b: z.number() });
    
    const schema = z.intersection(A, B);
    
    schema.parse({ a: "hello", b: 42 });  // ✅ Returns { a: "hello", b: 42 }
    schema.parse({ a: "hello" });          // ❌ ZodError: b is required
    schema.parse({ b: 42 });               // ❌ ZodError: a is required
    
    // Shorthand with .and()
    const same = A.and(B);
    



  24. z.literal()

  25. const schema = z.literal("hello");
    
    schema.parse("hello");   // ✅ Returns "hello"
    schema.parse("Hello");   // ❌ ZodError: Expected "hello"
    schema.parse("bye");     // ❌ ZodError
    
    // Other literal types
    z.literal(42).parse(42);         // ✅
    z.literal(true).parse(true);     // ✅
    z.literal(null).parse(null);     // ✅
    
    // Commonly used in discriminated unions
    const Event = z.discriminatedUnion("type", [
        z.object({ type: z.literal("click"), x: z.number(), y: z.number() }),
        z.object({ type: z.literal("keydown"), key: z.string() }),
    ]);
    



Modifiers

  1. Introduction



  2. .optional()

  3. const schema = z.string().optional();
    
    schema.parse("hello");    // ✅ Returns "hello"
    schema.parse(undefined);  // ✅ Returns undefined
    schema.parse(null);       // ❌ ZodError: Expected string, received null
    
    // In objects
    const User = z.object({
        name: z.string(),
        bio: z.string().optional(),
    });
    
    User.parse({ name: "Alice" });                  // ✅ { name: "Alice" }
    User.parse({ name: "Bob", bio: "Developer" }); // ✅ { name: "Bob", bio: "Developer" }
    


  4. .nullable()

  5. const schema = z.string().nullable();
    
    schema.parse("hello");    // ✅ Returns "hello"
    schema.parse(null);       // ✅ Returns null
    schema.parse(undefined);  // ❌ ZodError: Expected string, received undefined
    


  6. .nullish()

  7. const schema = z.string().nullish();
    
    schema.parse("hello");    // ✅ Returns "hello"
    schema.parse(null);       // ✅ Returns null
    schema.parse(undefined);  // ✅ Returns undefined
    schema.parse(123);        // ❌ ZodError
    


  8. .default()

  9. ZodType<T>.default(defaultValue: T): ZodDefault<ZodType<T>>
    ZodType<T>.default(defaultFn: () => T): ZodDefault<ZodType<T>>
    

    const schema = z.string().default("anonymous");
    
    schema.parse("Alice");     // ✅ Returns "Alice"
    schema.parse(undefined);   // ✅ Returns "anonymous"
    schema.parse(null);        // ❌ ZodError (null ≠ undefined)
    
    // Function form (avoids shared mutable reference)
    const withArray = z.array(z.string()).default(() => []);
    withArray.parse(undefined);  // ✅ Returns [] (new array each time)
    
    // In objects — missing keys are undefined
    const Config = z.object({
        port: z.number().default(3000),
        host: z.string().default("localhost"),
    });
    
    Config.parse({});  // ✅ { port: 3000, host: "localhost" }
    


  10. .catch()

  11. ZodType<T>.catch(catchValue: T): ZodCatch<ZodType<T>>
    ZodType<T>.catch(catchFn: (ctx: { error: ZodError; input: unknown }) => T): ZodCatch<ZodType<T>>
    

    const schema = z.string().catch("fallback");
    
    schema.parse("hello");     // ✅ Returns "hello"
    schema.parse(undefined);   // ✅ Returns "fallback"
    schema.parse(null);        // ✅ Returns "fallback"
    schema.parse(123);         // ✅ Returns "fallback"
    
    // Function form with context
    const smart = z.number().catch((ctx) => {
        console.log("Invalid input:", ctx.input);
        return 0;
    });
    smart.parse("bad");  // ✅ Returns 0, logs "Invalid input: bad"
    


    Input .default("x") .catch("x")
    undefined "x" "x"
    null ❌ throws "x"
    123 ❌ throws "x"


  12. .readonly()

  13. const schema = z.object({
        name: z.string(),
        tags: z.array(z.string()),
    }).readonly();
    
    type Result = z.infer<typeof schema>;
    // {
    //   readonly name: string;
    //   readonly tags: readonly string[];
    // }
    
    const data = schema.parse({ name: "Test", tags: ["a", "b"] });
    data.name = "New";      // ❌ TypeScript error: Cannot assign to 'name'
    data.tags.push("c");    // ❌ TypeScript error: Property 'push' does not exist
    
    // Runtime: no enforcement
    (data as any).name = "Mutated";  // Works at runtime
    



Object Utilities

  1. Introduction



  2. .extend()

  3. const Base = z.object({ name: z.string() });
    const Extended = Base.extend({ age: z.number() });
    
    Extended.parse({ name: "Alice", age: 30 });  // ✅
    Extended.parse({ name: "Bob" });              // ❌ age required
    
    // Overwriting keys
    const Overwritten = Base.extend({ name: z.number() });
    Overwritten.parse({ name: 123 });  // ✅ name is now number
    


  4. .merge()

  5. const A = z.object({ a: z.string() });
    const B = z.object({ b: z.number() });
    const Merged = A.merge(B);
    
    Merged.parse({ a: "hello", b: 42 });  // ✅
    Merged.parse({ a: "hello" });          // ❌ b required
    
    // Type: { a: string; b: number }
    



  6. .pick()

  7. const User = z.object({
        id: z.number(),
        name: z.string(),
        email: z.string(),
        password: z.string(),
    });
    
    const PublicUser = User.pick({ name: true, email: true });
    
    PublicUser.parse({ name: "Alice", email: "a@b.com" });  // ✅
    PublicUser.parse({ name: "Alice", email: "a@b.com", password: "x" });
    // ✅ Returns { name, email } — extra keys stripped
    


  8. .omit()

  9. const User = z.object({
        id: z.number(),
        name: z.string(),
        password: z.string(),
    });
    
    const SafeUser = User.omit({ password: true });
    
    SafeUser.parse({ id: 1, name: "Alice" });  // ✅
    // Type: { id: number; name: string }
    


  10. .partial()

  11. ZodObject<T>.partial(): ZodObject<{ [K in keyof T]: ZodOptional<T[K]> }>
    ZodObject<T>.partial<K extends keyof T>(keys: { [k in K]: true }): ZodObject<...>
    

    const User = z.object({
        name: z.string(),
        age: z.number(),
    });
    
    const PartialUser = User.partial();
    PartialUser.parse({});                    // ✅
    PartialUser.parse({ name: "Alice" });     // ✅
    PartialUser.parse({ age: 30 });           // ✅
    
    // Partial specific keys
    const PartialAge = User.partial({ age: true });
    PartialAge.parse({ name: "Alice" });      // ✅ age optional
    PartialAge.parse({});                     // ❌ name required
    


  12. .deepPartial()

  13. const Schema = z.object({
        user: z.object({
            name: z.string(),
            address: z.object({
                city: z.string(),
            }),
        }),
    });
    
    const Deep = Schema.deepPartial();
    
    Deep.parse({});                          // ✅
    Deep.parse({ user: {} });                // ✅
    Deep.parse({ user: { address: {} } });   // ✅
    


  14. .required()

  15. ZodObject<T>.required(): ZodObject<{ [K in keyof T]: ZodNonOptional<T[K]> }>
    ZodObject<T>.required<K extends keyof T>(keys: { [k in K]: true }): ZodObject<...>
    

    const Base = z.object({
        name: z.string().optional(),
        age: z.number().optional(),
    });
    
    const Required = Base.required();
    Required.parse({ name: "Alice", age: 30 });  // ✅
    Required.parse({ name: "Alice" });            // ❌ age required
    
    // Specific keys
    const NameRequired = Base.required({ name: true });
    NameRequired.parse({ name: "Alice" });        // ✅ age still optional
    


  16. .passthrough()

  17. const schema = z.object({ name: z.string() }).passthrough();
    
    schema.parse({ name: "Alice", extra: "kept" });
    // ✅ Returns { name: "Alice", extra: "kept" }
    
    // Compare to default
    z.object({ name: z.string() }).parse({ name: "Alice", extra: "stripped" });
    // ✅ Returns { name: "Alice" } — extra removed
    


  18. .strict()

  19. const schema = z.object({ name: z.string() }).strict();
    
    schema.parse({ name: "Alice" });              // ✅
    schema.parse({ name: "Alice", extra: 1 });    // ❌ ZodError: Unrecognized key "extra"
    


  20. .strip()

  21. const passthrough = z.object({ a: z.string() }).passthrough();
    const stripped = passthrough.strip();
    
    stripped.parse({ a: "hi", b: "removed" });  // ✅ { a: "hi" }
    


  22. .keyof()

  23. const User = z.object({
        id: z.number(),
        name: z.string(),
        email: z.string(),
    });
    
    const UserKey = User.keyof();
    
    UserKey.parse("id");       // ✅
    UserKey.parse("name");     // ✅
    UserKey.parse("password"); // ❌ Invalid enum value
    
    // Type: "id" | "name" | "email"
    



Transformations & Refinements

  1. Introduction



  2. .transform()

  3. const schema = z.string().transform((val) => val.length);
    
    schema.parse("hello");   // ✅ Returns 5 (number)
    schema.parse("");        // ✅ Returns 0
    
    // String to Date
    const dateSchema = z.string().transform((val) => new Date(val));
    dateSchema.parse("2024-01-15");  // ✅ Returns Date object
    
    // Type changes
    type Input = z.input<typeof schema>;   // string
    type Output = z.output<typeof schema>; // number
    
    // Chained transforms
    const chain = z.string()
        .transform((s) => s.trim())
        .transform((s) => s.toUpperCase())
        .transform((s) => s.split(""));
    
    chain.parse("  hi  ");  // ✅ Returns ["H", "I"]
    


  4. .refine()

  5. ZodType<T>.refine(
        check: (value: T) => boolean | Promise<boolean>,
        message?: string | { message?: string; path?: (string | number)[]; params?: object }
    ): ZodEffects<ZodType<T>, T>
    

    const schema = z.string().refine((val) => val.includes("@"), {
        message: "Must contain @",
    });
    
    schema.parse("user@example.com");  // ✅
    schema.parse("invalid");            // ❌ ZodError: Must contain @
    
    // Multiple refinements
    const password = z.string()
        .min(8)
        .refine((val) => /[A-Z]/.test(val), "Must contain uppercase")
        .refine((val) => /[0-9]/.test(val), "Must contain number");
    
    password.parse("weakpass");   // ❌ Must contain uppercase
    password.parse("Weakpass");   // ❌ Must contain number
    password.parse("Strong1x");   // ✅
    
    // Object-level refinement
    const Form = z.object({
        password: z.string(),
        confirm: z.string(),
    }).refine((data) => data.password === data.confirm, {
        message: "Passwords don't match",
        path: ["confirm"],  // Error appears on confirm field
    });
    


  6. .superRefine()

  7. ZodType<T>.superRefine(
        fn: (value: T, ctx: RefinementCtx) => void | Promise<void>
    ): ZodEffects<ZodType<T>, T>
    

    const schema = z.string().superRefine((val, ctx) => {
        if (val.length < 3) {
            ctx.addIssue({
                code: z.ZodIssueCode.custom,
                message: "Too short",
            });
        }
        if (!val.includes("@")) {
            ctx.addIssue({
                code: z.ZodIssueCode.custom,
                message: "Must contain @",
            });
        }
    });
    
    schema.parse("ab");  // ❌ Two errors: "Too short" AND "Must contain @"
    
    // Abort early with fatal flag
    const abortEarly = z.string().superRefine((val, ctx) => {
        if (val.length === 0) {
            ctx.addIssue({
                code: z.ZodIssueCode.custom,
                message: "Required",
                fatal: true,  // Stops further refinements
            });
            return z.NEVER;
        }
    });
    


  8. z.preprocess()

  9. // Coerce string to number
    const numSchema = z.preprocess(
        (val) => (typeof val === "string" ? parseInt(val, 10) : val),
        z.number()
    );
    
    numSchema.parse("42");   // ✅ Returns 42
    numSchema.parse(42);     // ✅ Returns 42
    numSchema.parse("abc");  // ❌ NaN fails z.number()
    
    // Normalize empty strings to undefined
    const optionalString = z.preprocess(
        (val) => (val === "" ? undefined : val),
        z.string().optional()
    );
    
    optionalString.parse("");         // ✅ Returns undefined
    optionalString.parse("hello");    // ✅ Returns "hello"
    
    // Parse JSON
    const jsonSchema = z.preprocess(
        (val) => (typeof val === "string" ? JSON.parse(val) : val),
        z.object({ name: z.string() })
    );
    
    jsonSchema.parse('{"name":"Alice"}');  // ✅ { name: "Alice" }
    



  10. .pipe()

  11. // Transform then validate
    const schema = z.string()
        .transform((val) => val.length)
        .pipe(z.number().min(3));
    
    schema.parse("hello");  // ✅ Returns 5
    schema.parse("hi");     // ❌ ZodError: Number must be >= 3
    
    // Coerce and validate
    const dateSchema = z.coerce.date().pipe(
        z.date().min(new Date("2020-01-01"))
    );
    
    dateSchema.parse("2024-01-01");  // ✅
    dateSchema.parse("2019-01-01");  // ❌ Too early
    


  12. Comparison Summary

  13. Method When it runs Input Use case
    z.preprocess() Before validation unknown Coerce raw input
    .refine() After validation T Custom validation
    .superRefine() After validation T Multiple errors, complex logic
    .transform() After validation T Change value/type
    .pipe() After first schema Output of first Chain schemas



Parsing

  1. Introduction



  2. .parse()

  3. const schema = z.string();
    
    schema.parse("hello");   // ✅ Returns "hello"
    schema.parse(123);       // ❌ Throws ZodError
    
    // Catching errors
    try {
        schema.parse(123);
    } catch (e) {
        if (e instanceof z.ZodError) {
            console.log(e.errors);  // [{ code, message, path, ... }]
        }
    }
    


  4. .safeParse()

  5. ZodType<T>.safeParse(data: unknown):
        | { success: true; data: T }
        | { success: false; error: ZodError }
    

    const schema = z.number();
    
    const good = schema.safeParse(42);
    // { success: true, data: 42 }
    
    const bad = schema.safeParse("oops");
    // { success: false, error: ZodError }
    
    // Usage pattern
    const result = schema.safeParse(input);
    if (result.success) {
        console.log(result.data);  // Typed as number
    } else {
        console.log(result.error.errors);
    }
    


  6. .parseAsync()

  7. const schema = z.string().refine(async (val) => {
        const exists = await checkDatabase(val);
        return !exists;
    }, "Already taken");
    
    await schema.parseAsync("newuser");  // ✅ Returns "newuser"
    await schema.parseAsync("existing"); // ❌ Throws ZodError
    
    // Note: calling .parse() on async schema throws
    schema.parse("test");  // ❌ Throws: "Async refinement encountered"
    


  8. .safeParseAsync()

  9. ZodType<T>.safeParseAsync(data: unknown): Promise<
        | { success: true; data: T }
        | { success: false; error: ZodError }
    >
    

    const schema = z.string().transform(async (val) => {
        const user = await fetchUser(val);
        return user;
    });
    
    const result = await schema.safeParseAsync("user123");
    
    if (result.success) {
        console.log(result.data);  // User object
    } else {
        console.log(result.error);
    }
    


  10. When to Use Each

  11. Method Throws Async Use case
    .parse() Yes No Simple validation, let errors propagate
    .safeParse() No No Handle errors locally, form validation
    .parseAsync() Yes Yes Async refinements/transforms
    .safeParseAsync() No Yes Async + local error handling



Type Inference

  1. Introduction



  2. z.infer<>

  3. const UserSchema = z.object({
        name: z.string(),
        age: z.number(),
    });
    
    type User = z.infer<typeof UserSchema>;
    // { name: string; age: number }
    
    // With transforms — infers OUTPUT type
    const schema = z.string().transform((s) => s.length);
    type Result = z.infer<typeof schema>;  // number
    
    // With optionals
    const Config = z.object({
        port: z.number().default(3000),
        host: z.string().optional(),
    });
    type Config = z.infer<typeof Config>;
    // { port: number; host?: string | undefined }
    


  4. z.input<>

  5. const schema = z.string().transform((s) => s.length);
    
    type Input = z.input<typeof schema>;   // string
    type Output = z.output<typeof schema>; // number
    
    // With defaults
    const Config = z.object({
        port: z.number().default(3000),
    });
    
    type ConfigInput = z.input<typeof Config>;
    // { port?: number | undefined }  — port is optional in input
    
    type ConfigOutput = z.infer<typeof Config>;
    // { port: number }  — port is guaranteed in output
    


  6. z.output<>

  7. const schema = z.string().transform((s) => parseInt(s, 10));
    
    type Output = z.output<typeof schema>;  // number
    
    // z.infer and z.output are aliases
    type A = z.infer<typeof schema>;   // number
    type B = z.output<typeof schema>;  // number — same
    


  8. When Input ≠ Output

  9. Schema z.input<> z.output<> / z.infer<>
    z.string() string string
    z.string().transform(s => s.length) string number
    z.number().default(0) number | undefined number
    z.string().optional() string | undefined string | undefined
    z.coerce.number() unknown number


  10. How Type Inference Works

  11. // Simplified Zod structure
    class ZodType<Output, Input = Output> {
        _output!: Output;  // Phantom property (never assigned at runtime)
        _input!: Input;
    }
    
    // Type aliases that extract phantom types
    type infer<T extends ZodType<any, any>> = T["_output"];
    type input<T extends ZodType<any, any>> = T["_input"];