packages/docs-v3/MIGRATION.md
This is a migration guide to walk you through the process of upgrading to Zod 3.
Zod 2 is being retired and will not leave beta. This is due to some unintuitive ramifications of the transformers API: details here.
.strip() (the default), .passthrough(), and .strict()import { z } from 'zod'; instead of using import * as syntax..format() method to ZodError to convert the error into a strongly-typed, nested object: format methodor method to ZodType (the base class for all Zod schemas) to easily create union types like z.string().or(z.number())and method to ZodType (the base class for all Zod schemas) to easily create intersection typesz.setErrorMap(myErrorMap) to globally customize the error messages produced by Zod: setErrorMapMap and Set schemas..unwrap() method for retrieving the schema they wrap.The minimum TypeScript version is now 4.1 (up from 3.7 for Zod 2). Several features have been rewritten to use recursive conditional types, an incredibly powerful new feature introduced in TS4.1.
Transformers syntax. Previously, creating a transformer required an input schema, an output schema, and a function to transform between them. You created transformers like z.transform(A, B, func), where A and B are Zod schemas. This is no longer the case. Accordingly:
The old syntax is no longer available:
# not available
z.transformer(A, B, func);
A.transform(B, func)
Instead, apply transformations by simply using the .transform() method that exists on all Zod schemas.
z.string().transform((val) => val.length);
Under the hood, all refinements and transformations are executed inside a dedicated "ZodEffects" class. Post-parsing, ZodEffects passes the data through a chain of refinements and transformations, then returns the final value. As such, you can now interleave transformations and refinements. For instance:
const test = z
.string()
.transform((val) => val.length)
.refine((val) => val > 5, { message: "Input is too short" })
.transform((val) => val * 2);
test.parse("12characters"); // => 24
Type guards (the .check() method) have been removed. Type guards interact with transformers in unintuitive ways so they were removed. Use .safeParse instead.
Object merging now behaves differently. If you merge two object schema (A.merge(B)), the fields of B will overwrite the fields of A if there are shared keys. This is how the .extend method already works. If you're looking to create an intersection of the two types, use z.intersection(A, B) or use the new .and method (A.and(B)).
Default values: default value logic is now implemented inside a ZodDefault class, instead of using transformers. (In a previous alpha version of Zod 3, default values were implemented inside the ZodOptional class.)
There have been small internal changes to the ZodIssue subtypes. See the new subtypes in the Error Handling guide. This may impact user who have written a custom error maps. Most users will not be affected.