You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于祖先属性实现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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 05:21:14