如何基于祖先属性实现Yup Schema的.when风格验证?
方案1:扩展Yup的
.when方法支持深层绝对路径 原生Yup的.when默认仅支持当前schema层级内的属性引用,无法直接跨层级获取深层属性。你可以通过扩展Yup的Schema原型,实现一个支持绝对路径的.whenDeep方法,直接从完整表单值中读取依赖属性:
// 扩展Yup类型声明 declare module 'yup' { interface BaseSchema<T, C = object, M = object> { whenDeep( path: string, options: { is: any | ((value: any) => boolean), then?: (schema: this) => this, otherwise?: (schema: this) => this } ): this; } } // 实现扩展方法 Yup.addMethod(Yup.mixed, 'whenDeep', function(path, options) { return this.test({ name: `whenDeep-${path}`, test: function(value) { // 从自定义选项中获取完整表单值 const fullValues = (this.options as any).fullSchemaValue; // 解析深层路径(这里用lodash的get,也可以自行实现简单解析) const dependentValue = _.get(fullValues, path); // 判断条件是否满足 const conditionMet = typeof options.is === 'function' ? options.is(dependentValue) : _.isEqual(dependentValue, options.is); // 根据条件应用对应的schema规则 if (conditionMet) { return options.then ? options.then(this.clone()).isValidSync(value, this.options) : true; } else { return options.otherwise ? options.otherwise(this.clone()).isValidSync(value, this.options) : true; } } }); });
使用方式和原生.when完全一致,直接传入深层路径即可:
mySchemaAttribute: yup.string().whenDeep('ancestor.prop.foo.bar', { is: true, then: schema => schema.required('此字段必填'), otherwise: schema => schema.notRequired() })
注:如果不想引入lodash,可以自己实现路径解析函数,比如通过split('.')遍历对象层级,同时处理数组索引[0]这类格式。
方案2:将完整表单值传入Yup Context
你可以把完整表单值放到Yup官方的context参数中,替代自定义的fullSchemaValue,这样获取值的方式更符合Yup的规范:
修改你的自定义resolver:
return (values: Record<string, any>): FormErrors => { try { _schema.validateSync(values, { abortEarly: false, context: { fullValues: values } // 将完整值存入context }); return {}; } catch (_yupError: any) { const yupError: YupValidationResult = _yupError; const results: any = {}; yupError.inner.forEach((error: YupError) => { results[error.path.replaceAll('[', '.').replaceAll(']', '')] = error.message; }); return results; } }
之后在自定义test或扩展方法中,就可以通过this.context.fullValues获取完整表单值,代码更统一。
方案3:封装通用深层验证辅助函数
如果不想扩展Yup原型,可以封装一个复用性强的辅助函数,减少重复代码:
const testDeep = ( path: string, validator: (depValue: any, currentValue: any, fullValues: any) => boolean, message: string ) => { return yup.test({ name: `testDeep-${path}`, message, test: function(value) { const fullValues = (this.options as any).fullSchemaValue; const depValue = _.get(fullValues, path); return validator(depValue, value, fullValues); } }); };
使用时直接调用这个函数:
mySchemaAttribute: yup.string().concat( testDeep( 'someOtherLocation.otherProp', (depValue, currentValue, fullValues) => { return !fullValues.topLevelObject.otherLayer.mySchemaAttribute || depValue; }, 'This error message will show' ) )
额外提示
- 替换Mantine的
yupResolver后,要确保在所有表单实例中统一使用自定义resolver,避免验证逻辑不一致。 - 处理复杂嵌套路径时,优先使用成熟的工具函数(如lodash.get),避免手动解析路径时出现数组索引、空值等边界问题。
内容的提问来源于stack exchange,提问作者iamaword
相关产品推荐
相关产品推荐

