Refine validations on object definitions don't get triggered until all fields in the object exist.
See original GitHub issueApplies to zod v3. I have not tried the same scenario on older versions.
I have two dates, startDate
and endDate
and I want to verify that the former is always earlier than the latter. I tried doing something like this:
z.object({
name: z.string(),
startDate: z.date(),
endDate: z.date(),
})
.refine((data) => checkValidDates(data.startDate, data.endDate), 'Start date must be earlier than End date.')
The problem is that the .refine()
function defined on the object
type doesn’t get triggered until the all fields in the object are defined. So, imagine you have a form and user entered both startDate
and endDate
first, but in a wrong order. At this point, the validations for these dates do not trigger because user has not entered name
. Then, user goes ahead enters name
and user now sees the validations for the dates kicking in. This is very awkward experience.
I can see that in this issue #61, the example adds a .partial()
to get around this problem, but this really isn’t a solution because all of these fields are required in my case. It’d be nice if the .refine
function defined on each individual field had access to the current form values, but it has access to its own value only. Any ideas, or did I miss something?
Issue Analytics
- State:
- Created 2 years ago
- Reactions:12
- Comments:19
Top GitHub Comments
Let me reiterate the issue in more detail.
It may be by design that
refine
validations on az.object
do not trigger until all fields in the object are filled in. For example, this is probably desired behavior when validating a password and a password confirmation. In this case, we’d expect therefine
validations to trigger once both fields are filled in. However, this is only true if those are the only fields being validated. If this example had several more fields, then we still want to trigger the validation as soon as those two fields are filled in, not when all fields are provided.One solution is to create a nested object schema and use
refine
on that. e.g.Sure, this works, but what if there’s another field that depends on start and end dates? In my scenario, the start and end date are the dates of an event. I also have a due date field which specifies when an event registration is due. So, I need to make sure it’s earlier than the start date. Now, I need to wait until start and end dates are both filled in before using a refine validation. But, what I want is to trigger the validation as soon as start and due date are filled in. zod cannot currently handle this if I’m correct. Also, doing this breaks the structure, so it doesn’t play nice with Blitz.js.
My suggestion is to have something like yup.when which gives you access to values of other fields. To enable this, the
refine
function may also give you back a function that retrieves the value of a specified field. For example:This is my suggestion. By moving the refine into the field schema, then we can makes sure it gets triggered as soon as that field is modified. There’s also no need to create a nested object schema. If there’s an existing solution, then that’s great. Please let met know.
I hope I made my point clear enough. I’m not sure if this is technically possible with how zod is written, but this feels like such a basic scenario, so it’d be great support this.
I’m currently in the process of testing Zod to migrate from Yup. I love it so far, but the issue is also an irritant for us