Zod 验证架构使字段成为必需的字段,基于另一个数组字段

Zod validation schema make field required based on another array field

提问人:SevenSouls 提问时间:5/27/2023 更新时间:11/17/2023 访问量:3674

问:

我有一个带有数组字段和字符串字段的 Zod 验证架构:

const zodSchema = z.object({
  goals: z.array(z.string()).nonempty("At least one goal is required"),
  goals_other: z.string(),
});

如何使该字段仅在数组包含字符串“Other”时才必填?goals_othergoals

我尝试了如下所示的优化功能,但没有奏效

const zodSchema = z
  .object({
    goals: z.array(z.string()).nonempty("At least one goal is required"),
    goals_other: z.string(),
  })
  .refine((data) => (data.goals.includes("Other") ? true : false), {
    message: "Required, please specify other goals",
    path: ["goals_other"],
  });

任何帮助都是值得赞赏的!

ReactJS 验证 zod

评论


答:

2赞 Souperman 5/27/2023 #1

有一种简单的方法可以获得所需的行为,假设如果字段中存在错误数据,则当未包含在列表中时,如果架构无法解析。我将首先展示这种方法:goals_other'Other'

import { z } from "zod";

const zodSchema = z
  .object({
    goals: z.array(z.string()).nonempty("At least one goal is required"),
    goals_other: z.string().optional() // optional to avoid failing when it's missing
  })
  .superRefine(({ goals, goals_other }, ctx) => {
    // Here we add the extra check to assert the field exists when
    // the "Other" goal is present.
    if (goals.includes("Other") && goals_other === undefined) {
      ctx.addIssue({
        code: "custom",
        message: "Required, please specify other goals",
        path: ["goals_other"]
      });
    }
  });

console.log(zodSchema.safeParse({
  goals: ['test'],
})); // success
console.log(zodSchema.safeParse({
  goals: ['Other'],
})); // failure
console.log(zodSchema.safeParse({
  goals: ['Other'],
  goals_other: 11,
})); // failure (because goals_other is not a string)
console.log(zodSchema.safeParse({
  goals: ['Other'],
  goals_other: 'test',
})); // success

但是IMO对此存在一个问题:

zodSchema.safeParse({
  goals: ['test'],
  goals_other: 11,
}); // Failure

这种解析尝试是失败的,因为该字段应该是 a 并且它得到了一个数字。如果你真的不关心这个字段,除非你的目标列表中有目标,那么你真正想要的是忽略这个字段,直到你找到另一个字符串,然后进行验证。goals_otherstring | undefinedgoals_other"Other"

const zodSchema = z
  .object({
    goals: z.array(z.string()).nonempty("At least one goal is required"),
    goals_other: z.unknown(),
  })
  .superRefine(({ goals, goals_other }, ctx) => {
    if (goals.includes("Other")) {
      if (goals_other === undefined) {
        ctx.addIssue({
          code: "custom",
          message: "Required, please specify other goals",
          path: ["goals_other"]
        });
      } else if (typeof goals_other !== 'string') {
        ctx.addIssue({
          code: 'custom',
          message: 'expected a string',
          path: ['goals_other'],
        });
      }
    }
  });

此架构将正确分析,但不会优化输出类型。 会有有点无益的类型。你可以用 a 中的类型断言来解决这个问题,但这感觉也有点尴尬。最好继续使用第一个架构,并接受当不存在该字段时,您实际上并没有忽略该字段。只要其他数据没有出现在您正在解析的值中,您就不会有问题。other_goalsunknowntransformgoals_other"Other"