Yup 简单校验也报 async function passed to sync validator
Yup 1.7.1 的 Standard Schema 入口始终返回 Promise,即使规则没有异步 test。把 schema 放到表单的异步入口,分清验证错误与提交数据转换。
表单只校验一个必填字符串,也没有发网络请求,把 Yup schema 放进 TanStack Form 的同步 onChange 后,却报 Error: async function passed to sync validator。先检查调用的是 Yup 自己的同步方法,还是 Standard Schema 入口;两者不是同一个执行约定。
Yup 1.7.1 的 schema['~standard'].validate() 明确是 async 方法,内部等待 schema.validate()。即使所有字段规则都能同步完成,这个适配层仍返回 Promise。规则简单,并不能让同步表单校验器直接接住它。
把 schema 放到表单的异步入口
TanStack Form 提供表单级与字段级的异步校验入口。下面用自定义 Hook 展示配置,schema 校验整个表单对象:
import { useForm } from '@tanstack/react-form';
import { object, string } from 'yup';
const userSchema = object({
email: string().email('邮箱格式不正确').required('请输入邮箱'),
});
export function useUserForm() {
return useForm({
defaultValues: { email: '' },
validators: {
onChangeAsync: userSchema,
},
});
}
不要同时把同一份 schema 留在原来的同步 onChange 中,否则那条失败路径还在。字段级配置则应使用对应字段的 schema;把整个对象 schema 填给只接收字符串的字段校验,会变成另一种类型错误。
这项调整需要核对项目实际安装的 TanStack Form 是否支持异步 Standard Schema。当前文档提供该入口,不能据此认定任意旧版适配器也能直接照用。Yup 1.7.1 的 Promise 行为本身仍然存在,不是换一个调用位置后变成了同步方法。
已有同步规则可以保留,但要知道同步失败时,异步规则默认可能不会继续运行。只有产品确实要求两条路径都执行,才考虑对应的 asyncAlways 选项。防抖用于减少重复调用,不负责把 Promise 转成同步结果。
必须同步时,单独调用 validateSync
如果这个入口必须立即得到结果,而且整份 Yup schema 确实没有异步 test,可以自行编写同步函数,调用 validateSync() 并把 ValidationError 按表单当前的错误协议返回。这是独立适配,不是修改 ~standard 的返回类型。
遇到异步 test 时,validateSync 不能替它等待。不要只做 TypeScript 类型断言,也不要把 Promise 对象当作校验成功;更不能捕获所有异常后返回“无错误”。预期的字段错误与适配器、程序内部异常应分开处理。
检查时保留空邮箱、格式错误和合法邮箱三类输入,等待异步校验结束后再判断表单错误状态。合法结果不能残留旧错误,错误输入不能提交;快速连续修改字段时,还要确认旧结果不会覆盖新值。TanStack Form 的 schema 校验不会自动把 Yup 的转换结果写回表单值,提交数据若依赖 trim、默认值或类型转换,应沿原提交流程单独取得并使用解析结果。


