Skip to Content

Last Updated: 3/12/2026


Type Inference

One of Zod’s most powerful features is its ability to infer static TypeScript types directly from schema definitions. This eliminates the need to define types separately and keeps your runtime validation in sync with your compile-time types.

Basic Inference with z.infer<>

Extract a TypeScript type from any schema using z.infer<typeof schema>:

import * as z from "zod"; const User = z.object({ id: z.number(), username: z.string(), email: z.string().email(), age: z.number().int().positive().optional(), }); type User = z.infer<typeof User>; /* { id: number; username: string; email: string; age?: number | undefined; } */ // Use the inferred type anywhere const user: User = { id: 1, username: "billie", email: "billie@example.com", };

Input vs. Output Types

Some schemas transform data during validation. For these, Zod distinguishes between input and output types:

const StringToNumber = z.string().transform((val) => val.length); type Input = z.input<typeof StringToNumber>; // string type Output = z.output<typeof StringToNumber>; // number // z.infer<> is an alias for z.output<> type Output2 = z.infer<typeof StringToNumber>; // number

Use z.input<> when you need the type before transformation, and z.output<> (or z.infer<>) for the type after transformation.

Why Inference Matters

Without Zod, you’d define types and validation separately:

// Without Zod: types and validation drift apart interface User { id: number; username: string; email: string; } function validateUser(data: unknown): User { // Manual validation logic that might not match the interface if (typeof data !== 'object' || data === null) throw new Error("Invalid"); // ... return data as User; }

With Zod, your schema is the single source of truth:

// With Zod: one definition, always in sync const User = z.object({ id: z.number(), username: z.string(), email: z.string().email(), }); type User = z.infer<typeof User>; const user = User.parse(untrustedData); // Type: User

Complex Type Inference

Zod correctly infers types for complex schemas:

const Post = z.object({ title: z.string(), author: User, // Nested schema tags: z.array(z.string()), metadata: z.record(z.string(), z.unknown()), status: z.enum(["draft", "published", "archived"]), }); type Post = z.infer<typeof Post>; /* { title: string; author: User; tags: string[]; metadata: Record<string, unknown>; status: "draft" | "published" | "archived"; } */

What’s Next