Home > Back-end >  zod conditional validation base on form field
zod conditional validation base on form field

Time:10-22

I have a status = 'DRAFT' | 'READY' enum field in my form, is it possible to change validation in zod lib based on value of this field?

// validation passed
{
  name: "John",
  surname: null,
  status: "DRAFT"
}

// validation failed
{
  name: "John",
  surname: null,
  status: "READY"
}

So esentialy if status === "READY" remove .min(1) from here

const schema = z.object({
  name: z.string(),
  surname: z.string().min(1),
  status: z.enum(["READY", "DRAFT"])
});

CodePudding user response:

This works

const schema = z.union([
  z.object({
    name: z.string(),
    surname: z.string(),
    status: z.literal("DRAFT"),
  }),
  z.object({
    name: z.string(),
    surname: z.string().min(1),
    status: z.literal("READY"),
  }),
]);

CodePudding user response:

The answer you came up with using unions is possible. An alternative that might yield better types in the future is to take advantage of zod's discriminatedUnion schema:

// placing shared fields in one place to avoid repetition
const base = z.object({
  name: z.string(),
});

const schema = z.discriminatedUnion(
  'status',
  [
    z.object({
      status: z.literal("DRAFT"),
      surname: z.string(),
    }).merge(base),
    z.object({
      status: z.literal("READY"),
      surname: z.string().min(1),
    }).merge(base),
  ],
);

In this case I believe you'll end up with the same inferred types as you would using union directly, however, this approach is slightly more resilient to other changes you might want to discriminate between. For example if the DRAFT object allowed surname to be optional then using the discriminated union would me that you could refine to non-optional types if status is "READY".

With union definition, the type for surname in that example would always be string | undefined regardless of the value of status.

  • Related