Home > database >  Using form validator ZOD's discriminatedUnion() function to distinguish between 3 forms
Using form validator ZOD's discriminatedUnion() function to distinguish between 3 forms

Time:10-18

ValidationSchema = z.object({
AuthenticationBlock: z.object({
  ChoiceOfForm: z.enum()
  DataBlock: z.discriminatedUnion(ChoiceOfForm, [
     z.object({ ChoiceOfForm = 1, empty fields}) //corresponds to the basic form
     z.object({ ChoiceOfForm = 2, just username/pw fields) //corresponds to less basic form
     z.object({ ChoiceOfForm = 3, user/pw   many fields}) //corresponds to advanced form
    ])
  })
})

Example of an object:

export type AuthenticationFormValues = {
    AuthenticationBlock?: {
        choice?: AuthenticationFormChoice | undefined;
        data: {
            password?: string;
            username?: string;
            formAuthenticationConfig?: {
                formUrl?: string;
                javaScriptLoadingDelayInMilliseconds?: number;
                forceLogin?: boolean;
                customLoginSequence?: string;
                authenticationFailed?: FormAuthenticationFailedConfiguration;
            };
        };
    };
};

choiceOfForm is an ENUM corresponding to 3 radio buttons that collapse different forms (1, 2 or 3). I would like to use ZOD's discriminatedUnion() function to enable the correct form validation block depending on which choiceOfForm is selected (1,2 or 3). Is there a way to pass the choiceOfForm variable to the discriminatedUnion(choiceOfForm, ...) to do this?

Thank you

CodePudding user response:

I think you can achieve what you're looking for with something like:

import { z } from "zod";

enum ChoiceOfForm {
  Option1 = 1,
  Option2 = 2,
  Option3 = 3
}

// Defining this once since it's shared between two of the arms.
const PasswordFields = z.object({
  password: z.string(),
  email: z.string().email()
});

const schema = z.discriminatedUnion("choice", [
  z.object({
    choice: z.literal(undefined),
  }),
  z.object({
    choice: z.literal(ChoiceOfForm.Option1)
  }),
  z.object({
    choice: z.literal(ChoiceOfForm.Option2)
  }).merge(PasswordFields),
  z.object({
    choice: z.literal(ChoiceOfForm.Option3),
    extraOption: z.number()
  }).merge(PasswordFields)
]);

Zod's discriminatedUnion function takes the key you are going to discriminate on as the first argument and a list of schemas for each "branch" of the union. Note that virtually any object will be parsed successfully with this schema because of the z.literal(undefined) option. You may want to omit that as a possibility, but I'm not certain of your business requirements.

If you successfully parse something with this schema, you can check the value of choice with a switch or if statement and narrow the types as you would with a regular discriminated union.

  • Related