TypeScript Yup模式验证和类型推断

smdncfj3  于 2023-01-14  发布在  TypeScript
关注(0)|答案(1)|浏览(149)
    • bounty将在3天后过期**。回答此问题可获得+50的声誉奖励。learnbydoing正在寻找来自声誉良好来源的答案

我目前有以下数据类型:

type TPlan =
  | {
      type: "pro";
      content: { signedAt: string; expiresOn: string };
    }
  | {
      type: "default" | "regular";
      content: { signedAt: string };
    };

并考虑到前面的数据类型,我为对象的每个结构定义了一个模式:

const schemaA = object({
  type: mixed<"pro">().oneOf(["pro"]).defined(),
  content: object({
    signedAt: string().defined(),
    expiresOn: string().defined(),
  }),
});

const schemaB = object({
  type: mixed<"default" | "regular">().oneOf(["default", "regular"]).defined(),
  content: object({
    signedAt: string().defined(),
  }),
});

当我对模式进行单独推断时,数据类型是正确的,如下所示:

type TSchemaA = InferType<typeof schemaA>;
type TSchemaB = InferType<typeof schemaB>;

但是,当我使用.oneOf()方法定义验证模式时,TypeScript说数据类型不正确。

export const validationSchema = mixed<TSchemaA | TSchemaB>()
  .oneOf([schemaA, schemaB])
  .defined();

我的问题是,如何使用Yup创建一个考虑到初始类型(TPlan)的验证模式呢?因为除了需要验证模式来验证JSON对象之外,我还需要模式的推理是正确的。

6vl6ewon

6vl6ewon1#

export const validationSchema = mixed<TSchemaA | TSchemaB>()
  .oneOf([schemaA, schemaB])
  .defined();

这不是你想要的,它会创建一个yup模式,可以用来验证TSchemaA或TSchemaB,但是你不想验证模式,你想验证数据。
以下是我的建议:

const schemaPro = yup.object({
    type: yup.mixed<"pro">().oneOf(["pro"]).required(),
    content: yup
        .object({
            signedAt: yup.string().required(),
            expiresOn: yup.string().required(),
        })
        .required(),
});
const schemaDefaultOrRegular = yup.object({
    type: yup.mixed<"default" | "regular">().oneOf(["default", "regular"]).required(),
    content: yup.object({
        signedAt: yup.string().required(),
    }),
});

那么这个将针对多个架构进行验证的帮助器:

const validateOneOf = async (schemas: yup.AnySchema[], value: unknown): Promise<TPlan> => {
    for (const schema of schemas) {
        try {
            return await schema.validate(value, { stripUnknown: true });
        } catch (e) {
            // I know you can create a yup error, that concat all error for the loop. 
            // then you should be able to fire it at the end.
        }
    }

    throw new yup.ValidationError("No schema validated");
};

我已经铸造输出作为TPlan,这应该是确定的情况。
那就叫它:

try {
        const plan: TPlan = await validateOneOf([schemaPro, schemaDefaultOrRegular], {
            type: "default",
            content: { signedAt: undefined, expiresOn: "456" },
        });
  
    } catch (e) {
        console.log("ERROR " + e);
    }

相关问题