typescriptvalidationenumsdiscriminated-unionzod

Use zod discriminatedUnion with an enum discriminator without typing out all enum possibilities


I'm trying to use zod schema validation to validate some data that has different constraints based on the value of an enumeration field (prisma generated enum type). Basically it can take these two shapes:

{ discriminatorField: "VAL1", otherField: "any string" }

{ discriminatorField: "any other allowed string besides VAL1", otherField: undefined }

It seems this can be done with z.discriminatedUnion() in the following way:

const schema = z.discriminatedUnion("discriminatorField", [
   z.object({ discriminatorField: z.literal("VAL1"), otherField: z.string()}),
   z.object({ discriminatorField: z.literal("VAL2"), otherField: z.string().optional()}),
   // ... have to type out all possible enum values as literal conditions here?
])

This works, but you have to type out all possible enum values to discriminate on. I tried using z.nativeEnum(MyEnum) instead of z.literal("VAL2") in the code above, but zod then complains that the values are overlapping, which is of course true, but I hoped it would just use the first case that matches.


Solution

  • const schema = z.discriminatedUnion("discriminatorField", [
      z.object({ discriminatorField: z.literal("VAL1"), otherField: z.string() }),
      ...Object.values(MyEnum)
        .filter((enum) => enum !== "VAL1")
        .map((enum) =>
          z.object({
            discriminatorField: z.literal(enum),
            otherField: z.string().optional(),
          }),
        ),
    ]);