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

如何在Zod转换操作后执行Schema校验?

问题:用Zod解析字符串为对象并校验,同时保留safeParse功能

我有一个表示对象的Schema(Book),希望将特定格式的字符串解析成该对象并完成校验。目前的实现是在transform内部调用Book.parse,但这种方式会破坏safeParse的功能——一旦解析失败,转换过程会直接抛出异常,而不是返回结构化的错误结果。示例代码如下:

import { z } from "zod"
export const Book = z.object({
  type: z.literal("book"),
  title: z.string(),
  published: z.number().int().min(1900)
});

export const bookFromString = z.string().transform(s => {
  const regexp = /^(.*) (\d+)$/.exec(s);
  return Book.parse({
    type: "book",
    title: regexp[1],
    published: +regexp[2]
  })
})
console.log(bookFromString.safeParse("abc 180"))

有没有可行的替代实现方式?


解决方案

核心问题是transform内部直接调用parse会抛出异常,绕过了Zod的safeParse错误捕获机制。可以通过链式调用Zod的校验与转换方法,让整个流程由Zod统一管控,确保safeParse能正常返回结构化错误信息。

实现方式1:拆分校验与转换流程

通过refine先验证字符串格式,再转换为对象结构,最后用pipe连接Book Schema完成最终校验:

import { z } from "zod"

export const Book = z.object({
  type: z.literal("book"),
  title: z.string(),
  published: z.number().int().min(1900)
});

export const bookFromString = z.string()
  // 第一步:校验字符串格式是否匹配正则
  .refine(s => /^(.*) (\d+)$/.test(s), {
    message: "字符串需符合「书名 年份」格式"
  })
  // 第二步:将字符串转换为Book结构的原始数据
  .transform(s => {
    const [_, title, publishedStr] = /^(.*) (\d+)$/.exec(s);
    return {
      type: "book",
      title,
      published: parseInt(publishedStr, 10)
    };
  })
  // 第三步:用Book Schema校验转换后的数据
  .pipe(Book);

// 测试示例
console.log(bookFromString.safeParse("abc 180"));
// 输出:{ success: false, error: ZodError(包含年份小于1900的错误) }

console.log(bookFromString.safeParse("abc 2000"));
// 输出:{ success: true, data: { type: "book", title: "abc", published: 2000 } }

console.log(bookFromString.safeParse("invalid format"));
// 输出:{ success: false, error: ZodError(包含格式不匹配的错误) }

实现方式2:在transform内部处理错误并抛出ZodError

如果希望在transform内完成所有逻辑,可以手动捕获解析错误,抛出ZodError让safeParse捕获:

import { z, ZodError } from "zod"

export const Book = z.object({
  type: z.literal("book"),
  title: z.string(),
  published: z.number().int().min(1900)
});

export const bookFromString = z.string().transform(s => {
  const regexp = /^(.*) (\d+)$/.exec(s);
  if (!regexp) {
    // 格式不匹配时抛出自定义ZodError
    throw new ZodError([{
      code: "custom",
      path: [],
      message: "字符串格式不符合「书名 年份」要求"
    }]);
  }

  const bookData = {
    type: "book",
    title: regexp[1],
    published: +regexp[2]
  };

  // 用safeParse校验数据,避免抛出异常
  const parseResult = Book.safeParse(bookData);
  if (!parseResult.success) {
    throw parseResult.error;
  }

  return parseResult.data;
});

// 测试
console.log(bookFromString.safeParse("abc 180"));
// 输出:{ success: false, error: ZodError(包含年份校验错误) }

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 22:20:35