SponsorsTable of ContentsGetting StartedInstallationMigration guide
Usage
CoerceLiteralsStringsNumbersBigIntsNaNsBooleansDatesEnumsOptionalsNullablesObjectsArraysUnionsRecordsMapsSetsIntersectionsRecursive typesPromisesInstanceofFunctionsPreprocess
Schema methods
Custom schemas
Guides and Concepts
Error HandlingComparisonEcosystem
Contributing
ChangelogCode of ConductLICENSE
Links
Clerk
⌘+J

© 2025 Zod


Designed in Earth-616

Build by oeri

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"; // compiles

What 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>; // number

Writing generic functions

With TypeScript generics, you can write reusable functions that accept Zod schemas as parameters. This enables you to create custom validation logic, schema transformations, and more, while maintaining type safety and inference.

When attempting to write a function that accepts a Zod schema as an input, it's tempting to try something like this:

function inferSchema<T>(schema: z.ZodType<T>) {
  return schema;
}

This approach is incorrect, and limits TypeScript's ability to properly infer the argument. No matter what you pass in, the type of schema will be an instance of ZodType.

inferSchema(z.string());
// => ZodType<string>

This approach loses type information, namely which subclass the input actually is (in this case, ZodString). That means you can't call any string-specific methods like .min() on the result of inferSchema.

A better approach is to infer the schema as a whole instead of merely its inferred type. You can do this with a utility type called z.ZodTypeAny.

function inferSchema<T extends z.ZodTypeAny>(schema: T) {
  return schema;
}
 
inferSchema(z.string());
// => ZodString

ZodTypeAny is just a shorthand for ZodType<any, any, any>, a type that is broad enough to match any Zod schema.

The Result is now fully and properly typed, and the type system can infer the specific subclass of the schema.

Inferring the inferred type

If you follow the best practice of using z.ZodTypeAny as the generic parameter for your schema, you may encounter issues with the parsed data being typed as any instead of the inferred type of the schema.

function parseData<T extends z.ZodTypeAny>(data: unknown, schema: T) {
  return schema.parse(data);
}
 
parseData("sup", z.string());
// => any

Due to how TypeScript inference works, it is treating schema like a ZodTypeAny instead of the inferred type. You can fix this with a type cast using z.infer.

function parseData<T extends z.ZodTypeAny>(data: unknown, schema: T) {
  return schema.parse(data) as z.infer<T>;
  //                        ^^^^^^^^^^^^^^ <- add this
}
 
parseData("sup", z.string());
// => string

Constraining allowable inputs

The ZodType class has three generic parameters.

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>'

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"
      }
  ] */
}

For detailed information about the possible error codes and how to customize error messages, check out the dedicated error handling guide: 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"]
}

On This Page

Guides and concepts
Type inference
Writing generic functions
Inferring the inferred type
Constraining allowable inputs
Error handling
Error formatting

Edit this page on GitHub