基于TS联合类型TPlan构建Yup验证Schema并实现类型推断
问题:基于TypeScript联合类型构建正确的Yup验证Schema
已有如下TypeScript联合类型TPlan:
type TPlan = | { type: "pro"; content: { signedAt: string; expiresOn: string }; } | { type: "default" | "regular"; content: { signedAt: string }; };
针对两种结构分别定义了Yup验证Schema:
import * as yup from 'yup'; const schemaA = yup.object({ type: yup.mixed<"pro">().oneOf(["pro"]).defined(), content: yup.object({ signedAt: yup.string().defined(), expiresOn: yup.string().defined(), }), }); const schemaB = yup.object({ type: yup.mixed<"default" | "regular">().oneOf(["default", "regular"]).defined(), content: yup.object({ signedAt: yup.string().defined(), }), });
单独对两个Schema做类型推断时结果正确,但尝试用.oneOf()合并为统一Schema时出现TypeScript类型错误:
// 错误用法,会导致类型不匹配 export const validationSchema = yup.mixed<TSchemaA | TSchemaB>() .oneOf([schemaA, schemaB]) .defined();
需要构建符合TPlan类型的正确Yup验证Schema,同时保证类型推断结果准确。
解决方案
错误原因
.oneOf()方法的作用是验证输入值属于指定的枚举值集合,而非用于合并多个验证Schema,直接传入Schema数组会导致类型不匹配。
方案一:使用yup.union(推荐)
Yup提供了union方法专门用于合并多个Schema,它会依次尝试用每个子Schema验证输入值,只要符合其中一个即可通过验证。
import * as yup from 'yup'; // 定义TPlan及子Schema(同上文)... // 使用union合并两个Schema export const validationSchema = yup.union([schemaA, schemaB]).defined(); // 类型推断结果与TPlan完全一致 type InferedTPlan = yup.InferType<typeof validationSchema>; // InferedTPlan 等价于 TPlan
方案二:自定义test验证逻辑
如果需要更灵活的验证控制,可以通过mixed().test()自定义测试函数,手动验证输入是否符合任一子Schema:
export const validationSchema = yup.mixed<TPlan>() .test({ name: 'plan-structure-validation', test: async (value) => { // 尝试用schemaA验证 try { await schemaA.validate(value, { abortEarly: false }); return true; } catch {} // 尝试用schemaB验证 try { await schemaB.validate(value, { abortEarly: false }); return true; } catch {} return false; }, message: '输入的计划结构不符合要求', }) .defined();
类型推断说明
- 使用
union方法时,InferType会自动将结果推断为两个子Schema的联合类型,完全匹配原始的TPlan。 - 使用自定义
test方式时,需要手动指定泛型为TPlan,确保类型推断结果准确。
内容的提问来源于stack exchange,提问作者user13695382
相关产品推荐
相关产品推荐

