You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何使用Zod条件验证与type数组对应的data数组元素?

Zod 实现数组对应位置的条件验证

需求说明

给定请求体中,type数组与data数组元素一一对应,需按以下规则验证:

  • type中为url的元素,对应data元素必须符合URL格式
  • type中为email的元素,对应data元素必须符合邮箱格式
  • type中为other的元素,对应data元素只需是字符串类型

实现思路

单独对data数组做验证无法关联对应位置的type值,必须在整个对象层面通过refine或superRefine做关联校验,同时支持自定义错误信息和错误定位。

完整代码实现

1. 基础验证方案(统一错误提示)

import { z } from "zod";

// 预定义各类型验证器,自带自定义错误信息
const urlValidator = z.string().url({ message: "INVALID_URL_FORMAT" });
const emailValidator = z.string().email({ message: "INVALID_EMAIL_FORMAT" });
const otherValidator = z.string({ message: "MUST_BE_STRING" });

const requestSchema = z.object({
  type: z.array(
    z.enum(["url", "email", "other"], { errorMap: () => ({ message: "CANNOT_BE_EMPTY" }) })
  ),
  data: z.array(z.string()),
}).refine((obj) => {
  // 先校验两个数组长度一致
  if (obj.type.length !== obj.data.length) return false;

  // 遍历对应位置元素做验证
  for (let i = 0; i < obj.type.length; i++) {
    const type = obj.type[i];
    const dataItem = obj.data[i];
    let isValid = true;

    switch (type) {
      case "url":
        isValid = urlValidator.safeParse(dataItem).success;
        break;
      case "email":
        isValid = emailValidator.safeParse(dataItem).success;
        break;
      case "other":
        isValid = otherValidator.safeParse(dataItem).success;
        break;
    }

    if (!isValid) return false;
  }
  return true;
}, {
  message: "DATA_MISMATCH_WITH_CORRESPONDING_TYPE",
  path: ["data"],
});

2. 细化错误方案(定位到具体元素)

如果需要给每个错误位置单独提示并精确定位,可以用superRefine实现:

import { z } from "zod";

const urlValidator = z.string().url({ message: "INVALID_URL_FORMAT" });
const emailValidator = z.string().email({ message: "INVALID_EMAIL_FORMAT" });
const otherValidator = z.string({ message: "MUST_BE_STRING" });

const requestSchema = z.object({
  type: z.array(
    z.enum(["url", "email", "other"], { errorMap: () => ({ message: "CANNOT_BE_EMPTY" }) })
  ),
  data: z.array(z.string()),
}).superRefine((obj, ctx) => {
  // 校验数组长度一致性
  if (obj.type.length !== obj.data.length) {
    ctx.addIssue({
      code: z.ZodIssueCode.custom,
      message: "TYPE_AND_DATA_ARRAY_LENGTH_NOT_MATCH",
      path: ["data"],
    });
    return;
  }

  // 逐个验证对应位置的元素
  for (let i = 0; i < obj.type.length; i++) {
    const type = obj.type[i];
    const dataItem = obj.data[i];

    switch (type) {
      case "url":
        const urlResult = urlValidator.safeParse(dataItem);
        if (!urlResult.success) {
          ctx.addIssue({
            ...urlResult.error.issues[0],
            path: ["data", i], // 错误定位到data数组的第i个元素
          });
        }
        break;
      case "email":
        const emailResult = emailValidator.safeParse(dataItem);
        if (!emailResult.success) {
          ctx.addIssue({
            ...emailResult.error.issues[0],
            path: ["data", i],
          });
        }
        break;
      case "other":
        const otherResult = otherValidator.safeParse(dataItem);
        if (!otherResult.success) {
          ctx.addIssue({
            ...otherResult.error.issues[0],
            path: ["data", i],
          });
        }
        break;
    }
  }
});

关键说明

  • 必须在对象层面做关联验证,因为单独验证data元素时无法获取对应位置的type值
  • 使用superRefine可以更灵活地添加多个错误,并且精确定位到数组的具体索引位置
  • 预定义的验证器可以复用,同时自带自定义错误信息,避免重复代码

内容的提问来源于stack exchange,提问作者charnould

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.27 12:23:14