T
TanStack2mo ago
like-gold

How should I handle my validator using Zod 4 when it uses z.coerce?

I want the form to load empty without anything preselected, and then enforce that the age is required. I'm upgrading from Zod 3 and it seems the v4 coerce returns unknown. All the validations work correctly, but I'm getting a type error
The types of 'input.age' are incompatible between these types. Type 'unknown' is not assignable to type 'number'.
This is my useForm hook
const form = useAppForm({
defaultValues: {
age: null as unknown as number,
},
validators: {
onSubmit: validationSchema,
},
})
const form = useAppForm({
defaultValues: {
age: null as unknown as number,
},
validators: {
onSubmit: validationSchema,
},
})
This is my validationSchema
export const validationSchema = z.object({
age: z.coerce
.number({
error: "Must provide your age",
})
.int()
.min(18, {
error: "Must be at least 18 to become an organ donor",
}),
});
export const validationSchema = z.object({
age: z.coerce
.number({
error: "Must provide your age",
})
.int()
.min(18, {
error: "Must be at least 18 to become an organ donor",
}),
});
I found that if I manually parse and return an error object in my onSubmit validator, it can avoid the ts error, but I feel like I'm missing an easier solution that doesn't need the custom function
onSubmit: ({ value }) => {
const result = validationSchema.safeParse(value)
if (!result.success) {
const fieldErrors: Record<string, { message: string }[]> = {}

for (const issue of result.error.issues) {
const path = issue.path.join('.')
fieldErrors[path] ||= []
fieldErrors[path].push({ message: issue.message })
}

return {
form: 'Form validation failed',
fields: fieldErrors,
}
}
}
onSubmit: ({ value }) => {
const result = validationSchema.safeParse(value)
if (!result.success) {
const fieldErrors: Record<string, { message: string }[]> = {}

for (const issue of result.error.issues) {
const path = issue.path.join('.')
fieldErrors[path] ||= []
fieldErrors[path].push({ message: issue.message })
}

return {
form: 'Form validation failed',
fields: fieldErrors,
}
}
}
3 Replies
deep-jade
deep-jade2mo ago
the reason it‘s unknown is because coerce‘s input could be anything. A simple way to get past that is to add a helper schema that just inputs string into whatever schema you want I can share my existing snippet for it tomorrow if you‘d like
like-gold
like-goldOP2mo ago
Yeah please do, thanks
complex-teal
complex-teal2mo ago
From the zod creator: https://github.com/react-hook-form/resolvers/issues/781#issuecomment-2993686064
If you want to modify the input type for a coerced schema, you can do this in Zod 4:
z.coerce.string<string>() // input and output type are both string`

Did you find this page helpful?