Generate Zod Union Types from Mixed JSON Arrays
Learn how to create Zod discriminated unions and regular unions from JSON arrays containing objects with different shapes.
Arrays
Detailed Explanation
Union Types in Zod
When a JSON array contains objects with different shapes, Zod provides z.union() and z.discriminatedUnion() to validate each variant correctly.
Example JSON
[
{ "type": "text", "content": "Hello world" },
{ "type": "image", "url": "https://cdn.example.com/photo.jpg", "alt": "A photo" },
{ "type": "video", "url": "https://cdn.example.com/clip.mp4", "duration": 120 }
]
Discriminated Union Schema
import { z } from "zod";
const textBlockSchema = z.object({
type: z.literal("text"),
content: z.string(),
});
const imageBlockSchema = z.object({
type: z.literal("image"),
url: z.string(),
alt: z.string(),
});
const videoBlockSchema = z.object({
type: z.literal("video"),
url: z.string(),
duration: z.number(),
});
const blockSchema = z.discriminatedUnion("type", [
textBlockSchema,
imageBlockSchema,
videoBlockSchema,
]);
type Block = z.infer<typeof blockSchema>;
discriminatedUnion vs union
z.discriminatedUnion() is faster and produces better error messages than z.union() because it uses the discriminant field to select the correct schema immediately, rather than trying each option.
| Feature | z.union() |
z.discriminatedUnion() |
|---|---|---|
| Requires shared key | No | Yes |
| Performance | Tries each option | Direct lookup |
| Error messages | Generic | Points to discriminant |
| Best for | Primitive unions | Object variants |
When to Use Each
z.discriminatedUnion()— Objects that share a common tag field (liketype,kind,action).z.union()— Primitive type unions or objects without a shared discriminant.
Example: Simple Union
// For primitives or objects without a discriminant
const idSchema = z.union([z.string(), z.number()]);
// Accepts either a string ID or numeric ID
Using discriminated unions is one of Zod's most powerful patterns for validating polymorphic API responses.
Use Case
You are building a rich content editor where the API returns an array of heterogeneous blocks (text, image, video) and you need validated, type-safe rendering logic for each variant.