Unions
Zod provides a built-in z.union
method for creating union types.
Basic Usage
ts
import { z } from "zod";
// Create a union type
const stringOrNumber = z.union([z.string(), z.number()]);
stringOrNumber.parse("hello"); // passes
stringOrNumber.parse(42); // passes
stringOrNumber.parse(true); // fails
Using .or() Method
For convenience, you can also use the .or()
method:
ts
const stringOrNumber = z.string().or(z.number());
const stringOrNumberOrBoolean = z.string()
.or(z.number())
.or(z.boolean());
Discriminated Unions
A discriminated union is a union type where each member has a common property with different literal values:
ts
const result = z.discriminatedUnion("status", [
z.object({
status: z.literal("success"),
data: z.string(),
}),
z.object({
status: z.literal("error"),
error: z.instanceof(Error),
}),
]);
// Validating success case
result.parse({
status: "success",
data: "It worked!",
}); // passes
// Validating error case
result.parse({
status: "error",
error: new Error("Something went wrong"),
}); // passes
Accessing Union Options
You can access all options in a union using the .options
property:
ts
const myUnion = z.discriminatedUnion("status", [
z.object({ status: z.literal("loading") }),
z.object({ status: z.literal("success") }),
z.object({ status: z.literal("error") }),
]);
console.log(myUnion.options); // get all options
Merging Discriminated Unions
To merge two or more discriminated unions, you can spread their .options
:
ts
const A = z.discriminatedUnion("status", [
z.object({ status: z.literal("loading") }),
z.object({ status: z.literal("success") }),
]);
const B = z.discriminatedUnion("status", [
z.object({ status: z.literal("error") }),
z.object({ status: z.literal("timeout") }),
]);
const Combined = z.discriminatedUnion("status", [
...A.options,
...B.options,
]);
Error Handling
For discriminated unions, Zod determines which schema to use for validation based on the discriminator field's value, providing more precise error messages:
ts
const result = z.discriminatedUnion("status", [
z.object({
status: z.literal("success"),
data: z.string(),
}),
z.object({
status: z.literal("error"),
message: z.string(),
}),
]);
// When providing incorrect fields
result.parse({
status: "success",
message: "This shouldn't be here", // Error: success status should have data field
}); // will throw specific error