Zod结合react-hook-form使用时,.default(false)被解析为boolean | undefined导致类型不匹配问题
这个问题我之前也碰到过,其实是Zod和react-hook-form的类型系统设计差异导致的,不是bug,咱们一步步拆解:
问题根源
当你在Zod Schema里用.default(false)时,Zod的逻辑是:输入可以缺失这个字段,解析时会自动用默认值补全,所以Zod内部会把这个字段的输入类型标记为可选(boolean | undefined);但z.infer<typeof TestSchema>得到的是解析后的输出类型——因为默认值会补全,所以输出里这个字段是必填的boolean。
而react-hook-form的zodResolver需要同时匹配「用户输入的类型」和「解析后的输出类型」,当你手动给useForm指定TestType(输出类型)时,resolver期望的输入类型(允许缺失)和你指定的表单类型(必填)就产生了冲突,于是出现了那个类型不兼容的报错。
解决方案
有两种简单的解决方式,根据你的需求选就行:
方案1:让useForm自动推导类型(推荐)
不用手动给useForm指定泛型,让zodResolver自动帮你推导正确的表单类型——这样react-hook-form会自动对齐Zod的输入输出类型,不会有冲突:
const TestSchema = z.object({ isFeatured: z.boolean().default(false), }); // 去掉<TestType>泛型,让resolver自动推导 const form = useForm({ resolver: zodResolver(TestSchema), mode: 'onChange', defaultValues: { isFeatured: false, }, }); // 此时form的values类型会被正确推断为{ isFeatured: boolean }
方案2:把默认值移到react-hook-form的defaultValues里
如果你一定要手动指定泛型,可以去掉Zod的.default(),把默认值放在react-hook-form的defaultValues中,这样Zod的Schema会要求输入必须包含该字段,和你手动指定的TestType完全对齐:
const TestSchema = z.object({ isFeatured: z.boolean(), // 移除Zod的default设置 }); type TestType = z.infer<typeof TestSchema>; const form = useForm<TestType>({ resolver: zodResolver(TestSchema), mode: 'onChange', defaultValues: { isFeatured: false, // 用react-hook-form的defaultValues设置默认值 }, });
补充验证
你之前测试const myVar:TestType = {}报错是对的,因为TestType是Zod解析后的输出类型,确实要求isFeatured必填;而resolver报错里的{ isFeatured?: boolean | undefined }是Zod允许的输入类型(因为默认值会补全),这两个类型本来就不一样——所以手动指定泛型才会冲突,让自动推导或者调整默认值的位置就能解决问题。
内容来源于stack exchange

