
本文详解如何在 Joi 中正确配置依赖多个字段值(如 one 和 three 同时存在)才使目标字段(如 two)变为必填的条件验证逻辑,避免常见误用 .when() 嵌套对象导致的校验异常。
本文详解如何在 joi 中正确配置依赖多个字段值(如 `one` 和 `three` 同时存在)才使目标字段(如 `two`)变为必填的条件验证逻辑,避免常见误用 `.when()` 嵌套对象导致的校验异常。
在 Joi 中,when() 方法不支持直接传入一个 Joi 对象(如 Joi.object({ one: Joi.required(), three: Joi.required() }))作为条件判断依据——这正是你原始代码报错的根本原因。Joi 的 when() 仅接受单个字段名字符串或引用路径作为第一个参数,后续通过 is、then、otherwise 构建链式条件逻辑。
要实现「仅当 one 和 three 均被提供(非 undefined / null) 时,two 才为必填」,需采用嵌套 when() 的方式:先判断 one 是否存在,再在其 then 分支中进一步判断 three 是否存在。
以下是推荐的、语义清晰且兼容 Joi v17+ 的写法:
const Joi = require('@hapi/joi'); // 或使用 @joi/browser(浏览器环境)
const schema = Joi.object({
one: Joi.string(),
two: Joi.string()
.when('one', {
is: Joi.exist(), // one 存在(非 undefined & 非 null)
then: Joi.when('three', {
is: Joi.exist(), // 且 three 也存在
then: Joi.required(), // → two 必填
otherwise: Joi.optional().strip() // three 缺失时 two 可选(并自动忽略该字段)
}),
otherwise: Joi.optional().strip() // one 缺失时 two 可选
}),
three: Joi.string()
});✅ 关键要点说明:
-
Joi.exist()判断字段是否被显式提供(排除undefined和null),比Joi.required()更适合“存在性”条件判断; - 每层
when()必须明确otherwise分支,否则未覆盖场景下 Joi 可能沿用默认规则(如隐式required),导致意外报错; - 推荐搭配
.strip()使用,确保当条件不满足时,two字段即使传入也会被安全移除,避免污染数据; - 不要尝试用
Joi.object({...})作为when()的第一个参数——该用法无效且会引发不可预测行为。
? 验证示例:
console.log(schema.validate({ one: 'a', two: 'b', three: 'c' }));
// ✅ { error: null, value: { one: 'a', two: 'b', three: 'c' } }
console.log(schema.validate({ one: 'a', two: undefined, three: 'c' }));
// ❌ error: '"two" is required'
console.log(schema.validate({ one: undefined, two: 'b', three: 'c' }));
// ✅ { error: null, value: { three: 'c' } } —— two 被 strip 移除
console.log(schema.validate({}));
// ✅ { error: null, value: {} }这种嵌套条件模式虽略显冗长,但语义明确、可维护性强,是 Joi 官方推荐的多字段联动校验方案。如需更高阶逻辑(如任意 N 个字段组合),建议封装为自定义校验函数或升级至 @hapi/joi 的后继项目 @joi/object(若已迁移)。

















