Yup根据属性值条件扩展根Object Schema实现方案
问题原因
写法不生效的核心原因是:yup 的 .when() 方法设计用于调整已提前声明的字段的校验规则,不支持在条件回调中通过 .shape() 给当前层级动态追加未提前声明的新字段。
你写的嵌套版本能运行,是因为提前在根层声明了extendedFields这个对象字段,.when()只是修改这个已存在字段的内部结构,符合API设计逻辑;但直接在根schema上调用.when()动态追加根级字段,属于在schema初始化完成后修改根结构,不会被yup识别。
可行实现方案
不需要通过根级when动态合并shape,只需要提前在根层声明所有可能出现的字段,通过when控制字段是否参与校验即可,同时完全保留baseSchema的复用性:
单字段复用写法
如果复用的字段数量少,可以直接引用baseSchema内的字段规则:
// 可独立复用的基础Schema const baseSchema = yup.object({ firstName: yup.string().required() }).required(); const TRUE_FLASE = ["true", "false"]; const extendedSchema = yup.object({ hasOtherFields: yup.mixed().required().oneOf([...TRUE_FLASE]), // 提前声明firstName字段,复用baseSchema内的规则,通过when控制校验逻辑 firstName: baseSchema.fields.firstName.when("hasOtherFields", { is: (val) => val === "true", then: (schema) => schema.required(), // 当hasOtherFields为false时,剔除firstName字段,不参与校验和结果返回 otherwise: (schema) => schema.strip() }) }).required();
多字段批量复用写法
如果baseSchema包含多个需要条件追加的字段,可以遍历字段批量加规则,不需要重复编写:
const TRUE_FLASE = ["true", "false"]; const baseSchema = yup.object({ firstName: yup.string().required(), // 其他可复用字段 }).required(); // 批量给baseSchema的所有字段加上条件校验逻辑 const conditionalBaseFields = Object.fromEntries( Object.entries(baseSchema.fields).map(([fieldKey, fieldSchema]) => [ fieldKey, fieldSchema.when("hasOtherFields", { is: (val) => val === "true", then: (schema) => schema.required(), otherwise: (schema) => schema.strip() }) ]) ); const extendedSchema = yup.object({ hasOtherFields: yup.mixed().required().oneOf([...TRUE_FLASE]), ...conditionalBaseFields }).required();
类型推导
上述写法可以直接通过yup自带的InferType推导出你期望的联合类型,不需要额外手动定义:
type SchemaType = yup.InferType<typeof extendedSchema>; // 推导结果等价于:{hasOtherFields: "false"} | {hasOtherFields: "true"; firstName: string}
内容的提问来源于stack exchange,提问作者Shumuu
相关产品推荐
相关产品推荐

