
使用 Yup 对 input type="number" 进行校验时,需同时处理“非数字输入”和“空值”两种错误场景;直接使用 yup.number().required().typeError() 无法区分二者,因空字符串会被 number() 类型校验直接拦截并仅触发 typeError。
使用 yup 对 `input type="number"` 进行校验时,需同时处理“非数字输入”和“空值”两种错误场景;直接使用 `yup.number().required().typeerror()` 无法区分二者,因空字符串会被 `number()` 类型校验直接拦截并仅触发 `typeerror`。
在表单验证中, 元素虽限制用户界面输入,但其 value 仍可能为 ""(空字符串)或非法字符串(如 "abc"、"12a"),而 Yup 的 yup.number() 默认将空字符串、null、undefined 等均视为类型不匹配,统一抛出 typeError,导致 required() 的自定义错误消息无法生效。
解决的关键在于分离类型判断逻辑:不再依赖 yup.number() 的内置类型约束,而是改用 yup.mixed() 放宽原始类型限制,再通过 .test() 自定义验证规则,分别响应两类错误:
- ✅ 空值(""、null、undefined)→ 触发“value is required”
- ❌ 非数字字符串(如 "abc"、"12.3.4")→ 触发“only numbers allowed”
- ✅ 有效数字(0、42、-5、"123")→ 通过校验
以下是推荐的完整 Schema 实现:
import * as yup from 'yup';
const schema = yup.object().shape({
fieldName: yup
.mixed()
.required('value is required')
.test(
'is-valid-number',
'only numbers allowed',
(value) => {
// 允许空值(交由 required 处理),只校验非空时是否为有效数字
if (value == null || value === '') return true;
// 使用 Number.isFinite 排除 NaN、Infinity、空格字符串等
const num = Number(value);
return Number.isFinite(num);
}
),
});⚠️ 注意事项:
- 不要混用 yup.number().required() 与自定义 test:number() 会提前转换并拒绝空值,使 required 消息失效;
- Number(value) 能正确处理字符串数字(如 "42" → 42),而 isNaN() 对空字符串返回 false(isNaN("") === false),易造成误判,故推荐 Number.isFinite();
- 若需支持小数或负数,当前逻辑已兼容(Number("-3.14") 和 Number("0.5") 均返回有限数值);
- 如需严格整数校验,可在 test 中追加 num % 1 === 0 判断。
总结:对原生数字输入控件做语义化校验,应以 mixed() 为基底,用 required() 管理空值提示,用 test() 精确控制数字合法性,从而实现清晰、可维护、符合 UX 需求的双重错误反馈。

















