如何使用Zod验证生成器及其输出?
使用Zod验证生成器及其输出的方案
Zod本身没有内置的z.generator()方法,但可以通过两种核心方式实现生成器的验证:一是校验生成器函数的返回类型,确保它返回符合要求的生成器;二是校验生成器产出的每一个值,实现快速失败。
1. 验证生成器函数的返回类型
用Zod的z.custom()自定义生成器类型的校验逻辑,确保函数返回的是合法生成器对象。
import { z } from "zod"; // 定义生成器产出值的Schema const SomeZodSchema = z.object({ type: z.enum(["progress", "result", "error"]), data: z.union([z.number(), z.string(), z.object({ message: z.string() })]) }); // 自定义生成器校验规则 const GeneratorSchema = z.custom<Generator<z.infer<typeof SomeZodSchema>>>( (val) => { return typeof val === "object" && val !== null && Symbol.iterator in val && typeof val.next === "function"; }, { message: "值必须是一个生成器对象" } ); // 定义作业函数的Schema const JobFunction = z .function() .args() // 有参数可在此定义参数Schema .returns(GeneratorSchema); // 合法作业函数示例 const validJob = function* () { yield { type: "progress", data: 50 }; yield { type: "result", data: "完成" }; }; JobFunction.parse(validJob); // 验证通过 // 非法函数示例(返回非生成器) const invalidJob = () => "不是生成器"; JobFunction.parse(invalidJob); // 抛出ZodError
2. 验证生成器产出的每个值
创建包装函数,在生成器产出值时自动用Zod校验,确保每一步产出都符合Schema,实现快速失败。
import { z } from "zod"; const SomeZodSchema = z.object({ type: z.enum(["progress", "result", "error"]), data: z.union([z.number(), z.string(), z.object({ message: z.string() })]) }); // 包装生成器,自动校验产出值 function validateGenerator<T>(generator: Generator<T>, schema: z.ZodSchema<T>) { return function* () { for (const value of generator) { // 校验失败立即抛出错误 const validated = schema.parse(value); yield validated; } } as Generator<z.infer<typeof schema>>; } // 作业函数示例 const myJob = function* () { yield { type: "progress", data: 50 }; yield { type: "result", data: "完成" }; // 下面这行会在产出时触发校验失败 // yield { type: "invalid", data: "错误类型" }; }; // 开启校验的生成器 const validatedGenerator = validateGenerator(myJob(), SomeZodSchema); // 遍历生成器,校验不通过时立即抛错 for (const item of validatedGenerator) { console.log(item); }
3. 结合两种方式:验证函数+自动校验产出
同时确保作业函数返回生成器,且每个产出值合法,把两种方式结合起来:
import { z } from "zod"; const SomeZodSchema = z.object({ type: z.enum(["progress", "result", "error"]), data: z.union([z.number(), z.string(), z.object({ message: z.string() })]) }); const GeneratorSchema = z.custom<Generator<z.infer<typeof SomeZodSchema>>>( (val) => typeof val === "object" && val !== null && Symbol.iterator in val && typeof val.next === "function", { message: "必须返回生成器对象" } ); const JobFunction = z .function() .args() .returns(GeneratorSchema); // 包装作业函数,返回带校验的生成器 function createValidatedJob(jobFn: z.infer<typeof JobFunction>) { JobFunction.parse(jobFn); // 先验证函数本身 return function* () { const generator = jobFn(); for (const value of generator) { yield SomeZodSchema.parse(value); } }; } // 合法作业函数 const validJob = function* () { yield { type: "progress", data: 75 }; yield { type: "result", data: "任务完成" }; }; const validatedJob = createValidatedJob(validJob); // 遍历产出,自动校验 for (const item of validatedJob()) { console.log(item); }
可观察对象的补充说明
如果用RxJS等可观察对象库,验证逻辑类似:通过pipe(map(value => schema.parse(value)))校验每个发射的值,非法值会立即抛出错误,同样符合快速失败的需求。
内容的提问来源于stack exchange,提问作者GregRos
相关产品推荐
相关产品推荐

