reactjs Zod验证模式根据另一个数组字段要求make字段

pn9klfpd  于 2023-05-28  发布在  React
关注(0)|答案(1)|浏览(138)

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

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

如何使goals_other字段仅在goals数组包含字符串“Other”时才是必需的?
我尝试了一个像下面这样的细化函数,但它不起作用

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"],
  });

任何帮助是赞赏!

kuhbmx9i

kuhbmx9i1#

有一种直接的方法可以获得您想要的行为,假设您可以接受当'Other'不包括在列表中时,如果goals_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

这个解析尝试失败了,因为goals_other字段应该是string | undefined,但它得到了一个数字。如果您真的不关心goals_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'],
        });
      }
    }
  });

这个模式可以正确地解析,但是不能细化输出类型。other_goals的类型是unknown,这有点没用。您可以在transform中使用类型Assert来解决这个问题,但这感觉也有点尴尬。最好继续使用第一个模式,并接受当"Other"不存在时实际上并没有忽略goals_other字段。只要其他数据没有出现在您解析的值中,就不会有问题。

相关问题