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>; // numberUse 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: UserComplex 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
- Common Patterns — Learn about optional, nullable, and default values
- Defining Schemas — Explore all available schema types