Joi验证TypeScript联合类型时如何获取嵌套错误而非通用类型不匹配错误
如何在Joi联合类型验证中获取嵌套字段的具体错误提示
我定义了一个TypeScript联合类型:
type PaymentTerm = | { type: 'AdvancePayment' } | { type: 'PaymentGoal'; netPaymentDays: number }
想用Joi的alternatives().try()来验证这个类型,写出了如下验证代码:
const schema = Joi.alternatives().try( Joi.object({ type: Joi.string().required().valid('AdvancePayment') }), Joi.object({ type: Joi.string().required().valid('PaymentGoal'), netPaymentDays: Joi.number().required().messages({ 'any.required': '{{#label}} is required' }) }) );
但当我执行验证:
const { error, value } = schema.validate({ type: 'PaymentGoal' });
预期会得到"netPaymentDays is required"的具体错误,实际却返回了通用的"value does not match any of the allowed types"错误。请问怎么才能让Joi返回嵌套字段的具体错误,而不是默认的类型不匹配提示?
这个问题我之前帮人排查过,核心原因是Joi默认在alternatives().try()的验证流程里,只要某一个备选schema验证失败,就会直接跳到下一个备选,不会保留这个schema里的具体字段错误;等所有备选都验证失败后,就抛出通用的类型不匹配提示。给你两种实用的解决办法:
方案一:用条件验证(更推荐)
相比直接用alternatives().try(),基于type字段做条件验证更贴合你这个联合类型的逻辑,而且能自然返回具体的字段错误:
const schema = Joi.object({ type: Joi.string().valid('AdvancePayment', 'PaymentGoal').required() }) .when('type', { is: 'PaymentGoal', then: Joi.object({ netPaymentDays: Joi.number().required().messages({ 'any.required': '{{#label}} is required' }) }) });
这样当你验证{ type: 'PaymentGoal' }时,Joi会先确认type字段符合要求,接着自动触发对netPaymentDays的校验,直接返回你预期的"netPaymentDays is required"错误,逻辑更清晰,错误提示也精准。
方案二:保留alternatives写法并自定义错误
如果你坚持要用alternatives().try()的写法,可以通过两个步骤实现需求:
- 验证时关闭
abortEarly选项,让Joi收集所有备选schema的错误 - 自定义
alternatives的错误消息,优先提取展示具体的字段错误
代码示例:
const schema = Joi.alternatives().try( Joi.object({ type: Joi.string().required().valid('AdvancePayment') }), Joi.object({ type: Joi.string().required().valid('PaymentGoal'), netPaymentDays: Joi.number().required().messages({ 'any.required': '{{#label}} is required' }) }) ) .messages({ 'alternatives.match': (ctx) => { // 从所有错误详情里找带具体字段路径的错误 const specificError = ctx.details.find(detail => detail.path.length > 0); // 有具体错误就返回它,没有就用默认提示 return specificError ? specificError.message : 'value does not match any of the allowed types'; } }); // 验证时开启完整错误收集,不要中途终止 const { error, value } = schema.validate({ type: 'PaymentGoal' }, { abortEarly: false });
这种方式会在所有备选都验证失败后,从收集到的错误里挑出最具体的字段错误返回,而不是默认的通用提示。
内容的提问来源于stack exchange,提问作者Alexander Zeitler
相关产品推荐
相关产品推荐

