zod icon indicating copy to clipboard operation
zod copied to clipboard

z.infer<T> infers dynamic fields are required even if required prop is false.

Open GuillermoMelean opened this issue 1 year ago • 2 comments

Environment:

  • zod v. 3.22.4
  • TypeScript v. 4.9.5

I have this props

interface Props {
  additional_questions?: Question[]
}

Question is this type

type TextQuestion = {
  id: number;
  title: string;
  type: "text";
  value: string;
  required?: boolean;
};

The property additional_questions is

[
 {
  id: 1, 
  title: "Idade", 
  value: "", 
  type: "text", 
  required: false 
 }
]

I have this zod form scheme

const formSchema = z.object({
    email: z.string().email("E-mail inválido"),
    names: z.string().min(1, "Campo obrigatório"),
    surnames: z.string().min(1, "Campo obrigatório"),
    phonenumber: z.string(),
    attendance: z.enum(["yes", "no"], {
      required_error: "Precisa de selecionar se aceita ou não o convite.",
    }),
    message: z.string(),
    additional_questions: z.array(
      z.object({
        id: z.number(),
        answer: z.string().min(1),
        title: z.string(),
        type: z.string(),
        required: z.boolean()
      })
    ),
  });

and this is the form:

const form = useForm<z.infer<typeof formSchema>>({
    resolver: zodResolver(formSchema),
    defaultValues: {
      email: "",
      names: "",
      surnames: "",
      phonenumber: "",
      attendance: "yes",
      message: "",
      additional_questions: props.additional_questions
    },
  });

And this is the render section:

{props.additional_questions && props.additional_questions?.map((question, index) => (
  <FormField
    key={question.id}
    control={form.control}
    name={`additional_questions.${question.id}.id`}
    render={({ field }) => (
      <FormItem>
        <FormLabel>{question.title}:</FormLabel>
        <FormControl>
          {
            question.type == "text" ? <Input disabled={loading} {...field} /> :
              question.type == "textarea" ? <TextArea rows={5} disabled={loading}  {...field} /> :
                <div></div>
          }

        </FormControl>
        <FormMessage />
      </FormItem>
    )}
  />
))}

I don't know why when I try to submit the form all the additional questions are required even if the props.additional_questions has questions with required a false.

Screenshot 2024-03-20 at 12 37 24

Can you help me?

GuillermoMelean avatar Mar 20 '24 12:03 GuillermoMelean

I think you just need to add .optional() to your additional_questions field, like the following.

const formSchema = z.object({
    email: z.string().email("E-mail inválido"),
    names: z.string().min(1, "Campo obrigatório"),
    surnames: z.string().min(1, "Campo obrigatório"),
    phonenumber: z.string(),
    attendance: z.enum(["yes", "no"], {
      required_error: "Precisa de selecionar se aceita ou não o convite.",
    }),
    message: z.string(),
    additional_questions: z.array(
      z.object({
        id: z.number(),
        answer: z.string().min(1),
        title: z.string(),
        type: z.string(),
        required: z.boolean()
      })
    ).optional(),
  });

Does that help?

imMatheus avatar Mar 20 '24 16:03 imMatheus

Hi. Thanks for you help, but i've already tried that before and it didn't work as I tought.

Here are some prints for evidence

Screenshot 2024-03-21 at 00 31 59 Screenshot 2024-03-21 at 00 31 39

Also...

I even tried to add .optional() in the z.object() and the component's behaviour was the same. Screenshot 2024-03-21 at 00 37 29

GuillermoMelean avatar Mar 21 '24 00:03 GuillermoMelean