尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Zod 数据校验实战:1 个 Schema 守住 3 种数据入口,类型自动推断

Zod 数据校验实战:1 个 Schema 守住 3 种数据入口,类型自动推断 Zod 数据校验实战1 个 Schema 守住 3 种数据入口类型自动推断【免费下载链接】zodTypeScript-first schema validation with static type inference项目地址: https://gitcode.com/GitHub_Trending/zo/zodZod 是 TypeScript 优先的 schema 数据校验库用一次声明定义结构parse即完成校验并返回强类型结果。本文从一个订单服务的真实痛点切入演示本地表单输入、外部接口数据、类型转换与跨字段规则四类数据入口的写法、性能手段和边界行为。场景是这样的你的订单服务订阅了支付平台的 webhook回调体在类型系统里是any。某天上游把amount从数字改成了字符串你的服务拿去做算术运算结果是一堆 0 和NaN落进数据库。防御手段不是在每个字段后手写if而是把回调结构声明成 schema进门的每一笔数据先过一遍校验——这正是 Zod 做的事schema 声明、运行时校验、静态类型推断三者由同一段定义产出不会漂移。 安装并跑通第一笔订单校验npm install zod仓库当前版本为 4.5.4零外部依赖README 标注的核心打包体积约 2kbgzip。最小可运行示例import * as z from zod; // 输入来自 HTTP 层的原始请求体类型上只是 unknown const raw { orderId: ORD-001, amount: 19.9, currency: CNY }; const Order z.object({ orderId: z.string().min(1), amount: z.number().positive(), currency: z.enum([CNY, USD, EUR]), }); const order Order.parse(raw); // 成功返回输入的深克隆且 TS 静态推断为 // { orderId: string; amount: number; currency: CNY|USD|EUR } // 失败抛 ZodErrorerr.issues 里带每个错误字段的路径与原因注意parse成功时返回的是输入的深克隆而不是原对象引用下游代码修改返回值不会污染上游数据。如果不想在入口处写try/catch用safeParse拿到判别联合类型{ success: true, data } | { success: false, error }由调用方决定是拒绝请求还是降级处理。类型不需要手写第二份type Order z.infertypeof Order提取输出类型如果后面接了transform还能用z.input/z.output分别取输入侧和输出侧类型两者可能不同。 按数据入口组织校验逻辑校验前端提交的订单表单前端提交的数据字段齐全但值不可信。除了基本类型检查min/max等约束直接链在 schema 上跨字段规则如折扣后金额不能为负用refine/superRefine补充后者能访问校验上下文并追加多条 issue适合一条规则对应多个报错位的场景。const CreateOrderForm z .object({ items: z.array(z.object({ sku: z.string(), qty: z.number().int().positive() })), discount: z.number().min(0).max(1).default(0), }) // 跨字段规则折后总额必须为正superRefine 可追加带 path 的多条 issue .superRefine((val, ctx) { const total val.items.reduce((s, it) s it.qty, 0) * (1 - val.discount); if (total 0) { ctx.addIssue({ code: custom, message: 折后金额必须为正, path: [items] }); } });default(0)的效果是输入里缺省discount时输出自动补 0下游类型里该字段不再是undefined。这类缺省即合法的字段用default而不是optional可以少写一层判空。守住外部接口的回调数据外部数据与本地输入的关键区别解析可能涉及异步逻辑比如refine里查数据库确认sku是否存在。此时同步的parse无能为力必须改用parseAsync/safeParseAsyncconst OrderEvent z.object({ orderId: z.string(), sku: z.string(), occurredAt: z.string().datetime(), // 严格 ISO 8601带毫秒/时区偏移会分别按参数校验 }); // 上游格式漂移时不让 worker 进程崩掉而是记录后走重试队列 const result await OrderEvent.safeParseAsync(await req.json()); if (!result.success) { audit(result.error.issues); // issues 带 path可直接落日志定位坏字段 return res.status(422).json({ issues: result.error.issues }); }z.string().datetime()的严格度值得留意默认只接受 UTCZ结尾的标准格式2020-10-14缺时间部分或00:00偏移写法都会被拒测试文件里对这类边界输入逐一做了断言见 packages/zod/src/v4/classic/tests/datetime.test.ts。如果上游确实给偏移时区需要显式传{ offset: true }。把字符串入口变成强类型表单、URL 参数、JSON 配置这三类来源的值几乎总是字符串但业务需要number/Date。z.coerce在校验前先做强制转换z.coerce.number()会把42变成42再校验。若转换逻辑本身不成立abc它会按NaN失败而不是悄悄放行——这是比手写Number(x)安全的地方。// 配置项env 里一切皆字符串输出侧统一为强类型 const AppConfig z.object({ port: z.coerce.number().int().min(1).max(65535), retryMs: z.coerce.number().int().positive(), }); // parse({ port: 8080, retryMs: 300 }) // { port: 8080, retryMs: 300 }需要更复杂的输入→输出映射例如把扁平字段拍平、把时间戳转Date时用transform它链在任意 schema 上并改变输出类型。跨字段与联合规则上面superRefine覆盖了字段间互相约束。再补两个高频形态字段本身可以是多种结构时用z.union或判别联合配合discriminatedUnion走更快分支要么是 A 对象要么是 B 对象这类 API 双版本响应union 的 issue 会列出每个分支各自的失败原因排查时不用猜走了哪条路径。⚡ 进阶性能手段与架构边界只关心合不合法而不需要错误详情时用顶层z.validate(schema, input)它返回布尔值、不构造任何错误对象文档给出的基准是比safeParse().success最高快 16 倍。热路径上更进一步的手段是z.compile(schema)——AOT 预编译快路径。官方 55 个 schema 的基准里中位加速约 2.4 倍复杂结构大对象数组、20 键对象接近 9 倍代价和限制如下表上生产前值得逐条对照限制行为含异步 refine/transform 的 schema无法编译z.compile原样返回 schema继续走常规解析器传{ strict: true }则抛错依赖new Function全局模式在z.config({ jitless: true })如 CSP 环境下自动关闭非法输入快路径与回退路径各跑一次refine/transform 可能执行两遍从编译后的 schema 派生.refine()/.extend()等产生的新 schema 未编译要对最终 schema 再 compile架构上理解一下这张图会省去很多困惑每个 schema 类由 Zod Core$ZodType基类 $ZodString等实现类和 Classic 层.optional()、.nullable()这类流式方法共同构成你日常用的ZodString继承两者。zod/mini子包只保留 Core 层去掉流式 API 换更小体积zod/v3子路径则供存量代码平滑过渡。 常见误区与边界情况parse成功不等于输入没被改动但成功值不是原引用——返回深克隆反过来修改返回对象不会影响传入的raw。依赖同一引用做副作用的代码会踩空。同步 API 混进异步 refine 后parse静默不执行异步逻辑必须换parseAsync/safeParseAsync这是运行时行为差异而非报错。v4 中部分字符串检查方法已弃用z.string().email()建议改用顶层z.email().datetime()建议改用z.iso.datetime()源码里两者都可用但弃用标记明确新代码直接用顶层形式。compile不是免费的对z.string()这类单节点 schema 编译没有收益无分发可消除且编译产物对非法输入会重复执行自定义逻辑refine 里写非纯函数如打日志、发请求要特别注意。.partial()不能用于含 refine 的 tuple会直接抛错——这是源码里显式写出的约束不是文档暗示。 仓库内延伸资源官方 API 入口packages/zod/src/index.tsv4 classic 对外暴露面schema 与检查项实现packages/zod/src/v4/classic/schemas.ts基础用法文档含z.validate说明packages/docs/content/basics.mdxcompile 原理与限制文档packages/docs/content/compile.mdx行为测试用例datetime 边界、error 输出、coerce 等packages/zod/src/v4/classic/tests/包能力总览README.md下一步把服务里一个最不可信的入口webhook 或前端提交接上safeParse把error.issues接进现有日志观察一周真实坏数据的分布。对热路径 schema 跑一次z.compile前后对比仓库自带基准工具见 packages/bench/确认加速是否值得引入。用z.infer替换一个手写接口类型检查输入/输出类型分叉处是否该改用z.input/z.output。【免费下载链接】zodTypeScript-first schema validation with static type inference项目地址: https://gitcode.com/GitHub_Trending/zo/zod创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表