I have a form with some fields and a switch which is used to show/hide some part of the form like this:
<Field name="field1" value={value1} />
<Field name="field2" value={value2} />
<Switch value={showShippingAddress} / >
{ showShippingAddress && (
<>
<Field name="field3" value={value3} />
<Field name="field4" value={value4} />
</>
)}
in the zod schema I would like to achieve something like this
z
.object({
field1: z.string()
field2: z.string(),
showShippingAddress: z.boolean(),
field3: showShippingAddressValue ? z.required : z.string(), // wrong just for describing desired result
field4: showShippingAddressValue ? z.string.required.regex('') : z.string(), // also wrong
The question is how can I make this in a better way, better than the only way I can see at the moment:
z
.object({
field1: z.string()
field2: z.string(),
})
.and(
z.object({
showShippingAddress: z.boolean(),
field3: z.string().nullable(),
field4: z.string().nullable(),
})
.superRefine(
({ showShippingAddress, field3, field4}, { addIssue }) => {
if(showShippingAddress && notEmptyCheckHelper(field3)) {
.addIssue({
code: z.ZodIssueCode.custom,
message: `Field3 is required.`
})
}
if(showShippingAddress && notEmptyCheckHelper(field4)) {
.addIssue({
code: z.ZodIssueCode.custom,
message: `Field4 is required.`
})
}
if(showShippingAddress && regexCheckHelper(field4)) {
.addIssue({
code: z.ZodIssueCode.custom,
message: `Field4 is not valid.`
})
}
// if there are more validations on Field 4 the checks here also grow and
// the same for more fields that depend on this showShippingAddress value
}))
Discriminated Unions are exactly what you are looking for.
You will need 2 objects, one with showShippingAddress as a literal 'true' and another with showShippingAddress as a literal 'false'. Then make a discriminated union.
So in your case, something like this should work
Do note that just using
z.string()
makes it a required field, while adding a .optional() will make the field optional.So the schema I provided above will successfully parse:
But it will fail to parse
Also, one extra note, if you don't want the strings to be empty, you may want to use the .nonempty() method