Guides and concepts
Type inference
You can extract the TypeScript type of any schema with z.infer<typeof mySchema> .
const A = z.string();
type A = z.infer<typeof A>; // string
const u: A = 12; // TypeError
const u: A = "asdf"; // compilesconst A = z.string();
type A = z.infer<typeof A>; // string
const u: A = 12; // TypeError
const u: A = "asdf"; // compilesWhat about transforms?
In reality each Zod schema internally tracks two types: an input and an output. For most schemas (e.g. z.string()) these two are the same. But once you add transforms into the mix, these two values can diverge. For instance z.string().transform(val => val.length) has an input of string and an output of number.
You can separately extract the input and output types like so:
const stringToNumber = z.string().transform((val) => val.length);
// ⚠️ Important: z.infer returns the OUTPUT type!
type input = z.input<typeof stringToNumber>; // string
type output = z.output<typeof stringToNumber>; // number
// equivalent to z.output!
type inferred = z.infer<typeof stringToNumber>; // numberconst stringToNumber = z.string().transform((val) => val.length);
// ⚠️ Important: z.infer returns the OUTPUT type!
type input = z.input<typeof stringToNumber>; // string
type output = z.output<typeof stringToNumber>; // number
// equivalent to z.output!
type inferred = z.infer<typeof stringToNumber>; // numberWriting generic functions
When attempting to write a function that accepts a Zod schema as an input, it's common to try something like this:
function makeSchemaOptional<T>(schema: z.ZodType<T>) {
return schema.optional();
}function makeSchemaOptional<T>(schema: z.ZodType<T>) {
return schema.optional();
}This approach has some issues. The schema variable in this function is typed as an instance of ZodType, which is an abstract class that all Zod schemas inherit from. This approach loses type information, namely which subclass the input actually is.
const arg = makeSchemaOptional(z.string());
arg.unwrap();const arg = makeSchemaOptional(z.string());
arg.unwrap();A better approach is for the generic parameter to refer to the schema as a whole.
function makeSchemaOptional<T extends z.ZodTypeAny>(schema: T) {
return schema.optional();
}function makeSchemaOptional<T extends z.ZodTypeAny>(schema: T) {
return schema.optional();
}
ZodTypeAnyis just a shorthand forZodType<any, any, any>, a type that is broad enough to match any Zod schema.
As you can see, schema is now fully and properly typed.
const arg = makeSchemaOptional(z.string());
arg.unwrap(); // ZodStringconst arg = makeSchemaOptional(z.string());
arg.unwrap(); // ZodStringConstraining allowable inputs
The ZodType class has three generic parameters.
class ZodType<
Output = any,
Def extends ZodTypeDef = ZodTypeDef,
Input = Output
> { ... }class ZodType<
Output = any,
Def extends ZodTypeDef = ZodTypeDef,
Input = Output
> { ... }By constraining these in your generic input, you can limit what schemas are allowable as inputs to your function:
function makeSchemaOptional<T extends z.ZodType<string>>(schema: T) {
return schema.optional();
}
makeSchemaOptional(z.string());
// works fine
makeSchemaOptional(z.number());
// Error: 'ZodNumber' is not assignable to parameter of type 'ZodType<string, ZodTypeDef, string>'function makeSchemaOptional<T extends z.ZodType<string>>(schema: T) {
return schema.optional();
}
makeSchemaOptional(z.string());
// works fine
makeSchemaOptional(z.number());
// Error: 'ZodNumber' is not assignable to parameter of type 'ZodType<string, ZodTypeDef, string>'Error handling
Zod provides a subclass of Error called ZodError. ZodErrors contain an issues array containing detailed information about the validation problems.
const result = z
.object({
name: z.string(),
})
.safeParse({ name: 12 });
if (!result.success) {
result.error.issues;
/* [
{
"code": "invalid_type",
"expected": "string",
"received": "number",
"path": [ "name" ],
"message": "Expected string, received number"
}
] */
}const result = z
.object({
name: z.string(),
})
.safeParse({ name: 12 });
if (!result.success) {
result.error.issues;
/* [
{
"code": "invalid_type",
"expected": "string",
"received": "number",
"path": [ "name" ],
"message": "Expected string, received number"
}
] */
}For detailed information about the possible error codes and how to customize error messages, check out the dedicated error handling guide: [Error handling](Error handling)
Zod's error reporting emphasizes completeness and correctness. If you are looking to present a useful error message to the end user, you should either override Zod's error messages using an error map (described in detail in the Error Handling guide) or use a third-party library like zod-validation-error
Error formatting
You can use the .format() method to convert this error into a nested object.
const result = z
.object({
name: z.string(),
})
.safeParse({ name: 12 });
if (!result.success) {
const formatted = result.error.format();
/* {
name: { _errors: [ 'Expected string, received number' ] }
} */
formatted.name?._errors;
// => ["Expected string, received number"]
}const result = z
.object({
name: z.string(),
})
.safeParse({ name: 12 });
if (!result.success) {
const formatted = result.error.format();
/* {
name: { _errors: [ 'Expected string, received number' ] }
} */
formatted.name?._errors;
// => ["Expected string, received number"]
}