Compose schemas with z.object(), z.union(), z.discriminatedUnion(), z.transform(), safeParse(), and z.infer for TypeScript integration
0 / 5 completed
1 / 5
How does z.discriminatedUnion() differ from z.union()?
z.discriminatedUnion(): Example: z.discriminatedUnion('type', [z.object({ type: z.literal('circle'), radius: z.number() }), z.object({ type: z.literal('rect'), width: z.number() })]). Zod reads the discriminant key first and selects the matching schema directly, avoiding the trial-and-error of z.union(). Error messages are scoped to the correct variant.
2 / 5
What does z.transform() do in Zod?
z.transform(): Chains after a schema: z.string().transform(s => parseInt(s, 10)) — input is string, output is number. The transformed type appears in the schema's output type. z.preprocess() runs before validation; transform() runs after. Use z.string().datetime().transform(s => new Date(s)) to safely parse ISO dates.
3 / 5
What does safeParse() return in Zod and why prefer it over parse()?
safeParse():const result = schema.safeParse(input). Check result.success before accessing result.data. This avoids wrapping in try/catch. Use in API handlers, form validation, and anywhere you want to inspect the ZodError (with result.error.issues) rather than catching a thrown error.
4 / 5
How do you make a Zod object schema strictly reject unknown keys?
.strict(): By default, z.object() strips unknown keys. Chain .strict() to reject them: z.object({ name: z.string() }).strict() — passing { name: 'Alice', extra: true } fails. Alternatively, .passthrough() keeps unknown keys in the output. .strip() (default) removes them silently.
5 / 5
How does z.infer<typeof schema> benefit TypeScript developers?
z.infer:const UserSchema = z.object({ id: z.number(), name: z.string() }); type User = z.infer<typeof UserSchema> — User is { id: number; name: string }. This eliminates duplication between validation schemas and TypeScript interfaces. Changes to the schema automatically propagate to the type.