I have a form field which may either have no input or it must contain a number.
Problem: Typescript complains about assigning an empty string "" as default value to my zod-typed property optionalNumber
. My zod schema allows a "" literal or a number as input.
Unfortunately, when using react-hook-form with zod, you get a react console warning at runtime, that you should not convert controlled components into uncontrolled components, if you have zod attributes which did not get a default value assigned in React.useForm(). So I believe I must really assign a default value to my zod optionalNumber
property.
My schema:
const mySchema = z.object(
optionalNumber: z.literal('')
.or(z.coerce.number())
.transform(((val) => val === '' ? undefined : val)
)
Problem: Typescript warning when assigning "" as default value:
const form = useForm(z.infer<typeof mySchema>({
resolver: zodResolver(mySchema),
defaultValues: {
optionalNumber: '' // TS2322: Type string is not assignable to type number
}
});
The cause of the warning appears to be the fact that the defaultValues
parameter of React.useForm()
considers the possible output type of zod parsing to be the allowed input type for the default value. Might be a fundamental issue, but I need to live in the react world that exists, not the one I would rather like ;-)
Is there a way to have a react-hook-form/zod number field, which gets initialized to have no input in the beginning, and can be submitted
- either without input
- or with a numeric input?
Note that the application does what I want, the problem is Typescript complaining about the wrong type of the default value of optionalNumber
.
EDIT: as @super and @nicholas-dullam pointed out: once you use z.transform()
, it is fundamental to distinguish input and output type, zod is perfectly able to do that with z.input<typeof mySchema>
and urgently asks us to do so: /?id=type-inference.
Possible solution along these lines:
Zod Schema:
price: z.string().optional().transform((val) => {
if(val === '' || typeof val === 'undefined') {
return undefined;
} else {
return parseInt(val, 10);
}
}).refine((val) =>
typeof val === 'undefined' || val >= 0, {
message: "Price must at least be 0"
})
Setting '' as default value in React.useForm()
:
const form = useForm(
z.input<typeof mySchema>({ // <== z.input does the trick
resolver: zodResolver(mySchema),
defaultValues: {
optionalNumber: ''
}
});
I have a form field which may either have no input or it must contain a number.
Problem: Typescript complains about assigning an empty string "" as default value to my zod-typed property optionalNumber
. My zod schema allows a "" literal or a number as input.
Unfortunately, when using react-hook-form with zod, you get a react console warning at runtime, that you should not convert controlled components into uncontrolled components, if you have zod attributes which did not get a default value assigned in React.useForm(). So I believe I must really assign a default value to my zod optionalNumber
property.
My schema:
const mySchema = z.object(
optionalNumber: z.literal('')
.or(z.coerce.number())
.transform(((val) => val === '' ? undefined : val)
)
Problem: Typescript warning when assigning "" as default value:
const form = useForm(z.infer<typeof mySchema>({
resolver: zodResolver(mySchema),
defaultValues: {
optionalNumber: '' // TS2322: Type string is not assignable to type number
}
});
The cause of the warning appears to be the fact that the defaultValues
parameter of React.useForm()
considers the possible output type of zod parsing to be the allowed input type for the default value. Might be a fundamental issue, but I need to live in the react world that exists, not the one I would rather like ;-)
Is there a way to have a react-hook-form/zod number field, which gets initialized to have no input in the beginning, and can be submitted
- either without input
- or with a numeric input?
Note that the application does what I want, the problem is Typescript complaining about the wrong type of the default value of optionalNumber
.
EDIT: as @super and @nicholas-dullam pointed out: once you use z.transform()
, it is fundamental to distinguish input and output type, zod is perfectly able to do that with z.input<typeof mySchema>
and urgently asks us to do so: https://zod.dev/?id=type-inference.
Possible solution along these lines:
Zod Schema:
price: z.string().optional().transform((val) => {
if(val === '' || typeof val === 'undefined') {
return undefined;
} else {
return parseInt(val, 10);
}
}).refine((val) =>
typeof val === 'undefined' || val >= 0, {
message: "Price must at least be 0"
})
Setting '' as default value in React.useForm()
:
const form = useForm(
z.input<typeof mySchema>({ // <== z.input does the trick
resolver: zodResolver(mySchema),
defaultValues: {
optionalNumber: ''
}
});
Share
Improve this question
edited Jan 20 at 14:22
dschulten
asked Jan 19 at 18:19
dschultendschulten
3,1222 gold badges33 silver badges46 bronze badges
6
|
Show 1 more comment
1 Answer
Reset to default 2z.infer<typeof schema>
will always resolve to the output of the provided zod schema (z.output<typeof schema>
). Using z.input<typeof schema>
i.e. useForm<z.input<typeof schema>>(...)
will resolve to your expected types.
z.input
instead ofz.infer
? – super Commented Jan 19 at 18:34