Reactjs中Yup schema动态添加字段isAdmin为false报错原因及解决
报错产生的核心原因
问题出在展开运算符的目标值类型不符合TypeScript的要求:
- 代码里写的
...(isAdmin && isFirstLogin) && {role: Yup.string()},整个作为展开目标的表达式结果不是固定的对象类型:- 当
isAdmin为false时,isAdmin && isFirstLogin会短路返回布尔值false,后续false && {role: ...}的最终结果也是布尔值false,属于非对象类型 - TypeScript 明确规定展开运算符只能作用在对象类型上,遇到非对象的布尔值时,就会抛出「Spread types may only be created from object types」的类型检查错误
- 当
- 当
isAdmin写死为true时,TypeScript的字面量类型推导会自动收敛结果中的非对象分支,暂时绕开了类型检查,所以没立刻报错,但这个写法本身存在逻辑隐患——条件不满足时本质是在对布尔值做展开操作,完全不符合对象展开的设计要求。
适配Yup场景的正确动态添加属性方式
写法1:通过三元表达式确保展开目标永远是对象类型
调整条件分支的返回值逻辑,让表达式最终满足“条件成立就返回带role校验规则的对象,不成立就返回空对象”的结构,从根源上避免对非对象类型做展开:
const schema = Yup.object({ fname: Yup.string().required('Required'), lname: Yup.string().required('Required'), ...(isAdmin && isFirstLogin ? { role: Yup.string() } : {}) })
这个写法和最初的预期逻辑完全一致,展开的目标不管条件成立与否都是纯对象类型,不会再触发TypeScript的类型报错。
写法2:借助Yup原生shape方法动态扩展(更推荐)
Yup的schema实例本身就提供了shape方法,专门用来在现有校验规则的基础上新增字段,这种写法的代码可读性和类型提示表现都更好:
// 先定义基础校验结构 let schema = Yup.object({ fname: Yup.string().required('Required'), lname: Yup.string().required('Required'), }) // 满足条件时动态追加角色字段的校验规则 if (isAdmin && isFirstLogin) { schema = schema.shape({ role: Yup.string().required('请选择角色') }) }
这种写法不需要手动处理对象展开的类型兼容问题,还能享受Yup完整的类型提示,后续给动态字段配置各类校验规则也更灵活。
补充提醒:直接把Yup校验规则作为普通对象属性并不能生成可用的校验schema,最终的校验结构一定要传入
Yup.object()做包装,才能被Yup正常识别和执行校验流程。
内容的提问来源于stack exchange,提问作者Passionate Coder
相关产品推荐
相关产品推荐

