如何在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
相关产品推荐
相关产品推荐

