Note

Distributed Zod discriminated unions

Here’s a trick that seems obvious in retrospect. If you want to represent a type like this in Zod:

type Order = { name: string } & (
  | { orderType: 'DELIVERY'; details: DeliveryOrderDetails }
  | { orderType: 'PICKUP'; details: PickupOrderDetails }
);TypeScript

You can just create the union type separately and then intersect it:

const OrderDetails = z.discriminatedUnion('orderType', [
  z.object({
    orderType: z.literal('DELIVERY'),
    details: DeliveryDetailsSchema,
  }),
  z.object({
    orderType: z.literal('PICKUP'),
    details: PickupDetailsSchema,
  }),
]);

const OrderSchema = z.object({ name: z.string() }).and(OrderDetails);

// Or this:
// const OrderSchema = z.intersection(z.object({ name: z.string() }), OrderDetails);TypeScript

Neat if you’re forced to keep the type discriminator top-level.