ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Zod 实战笔记:TypeScript 数据验证的 3 类数据来源与 3 个高频坑

Zod 实战笔记:TypeScript 数据验证的 3 类数据来源与 3 个高频坑 Zod 实战笔记TypeScript 数据验证的 3 类数据来源与 3 个高频坑【免费下载链接】zodTypeScript-first schema validation with static type inference项目地址: https://gitcode.com/GitHub_Trending/zo/zod订单接口少返回一个status字段前端一行order.status.toUpperCase()当场抛 TypeError整个列表页白屏——这类问题编译期类型拦不住。Zod 是一个 TypeScript 优先的模式声明与验证库用一份 schema 同时完成运行时校验和类型推断专门处理跨界数据的验证。先跑起来再讲为什么import * as z from zod; const User z.object({ name: z.string(), age: z.number().int().min(0) }); const input: unknown JSON.parse(rawBody); // 外部数据先当 unknown 对待 const result User.safeParse(input); // 不抛异常返回 success / data / error if (result.success) { console.log(result.data.name); // 类型推断为 string直接使用 } else { console.log(result.error.issues); // 每条错误带 path定位到字段 }上图是仓库文档里对三种入口的示意z.parse把unknown直接变成可信输出decode/encode则面向已有类型标注的输入输出做双向转换。Zod 的边界要清楚它是运行时的数据守门员不做数据库层校验不是 ORM也不帮你发请求。它的职责到这份数据符合约定为止之后的事交给业务逻辑。体积方面官方 README 标注核心 bundle gzip 后约 2KB。数据从哪来三个真实场景来自浏览器表单的不可信输入表单是典型不可信输入用户能改一切。Zod 表单验证在这里的写法是字段级约束加对象级兜底const Login z.object({ email: z.string().email(邮箱格式不正确), password: z.string().min(8, 密码至少 8 位), remember: z.boolean().default(false), // checkbox 不勾时提交的是 undefined靠 default 兜底 }); const values Login.parse(formData); // 通过校验的值才进入业务逻辑容易忽略的细节未勾选的 checkbox 提交上来是undefined而不是false这类字段应该用.default()而不是.optional()否则下游拿到的还是undefined。来自后端 API 的 JSON 响应后端响应同样不可信——接口文档是愿望不是契约。API 校验应该放在请求边界做一次而不是每个消费点各写一遍ifconst Order z.object({ id: z.string(), status: z.enum([paid, shipped, refunded]), // 枚举比字符串严拼错的 status 会被拦下 items: z.array(z.object({ sku: z.string(), qty: z.number().int() })), }); // 在请求边界校验一次后续消费点不再重复判断 const order Order.parse(await res.json());容易忽略的细节safeParse失败时error.issues里每条错误的path是数组形式嵌套字段如items[2].qty需要自己拼成可读路径再映射到 UI 的具体输入框上。跨服务 / 跨团队共享的数据契约事件流、消息队列里的数据来自别的团队对方改一个字段的成本由你承担。契约要窄只写双方确认过的字段并把语义写进字段名。// 字段名把单位写死避免元/分、毫秒/秒这类争议 const InvoiceEvent z.object({ kind: z.literal(invoice.paid), invoiceId: z.string(), amountCents: z.number().int(), // 金额用整数分绕开浮点误差 }); // 事件流里混着多团队的事件先按 kind 过滤再验证 const event raw.kind invoice.paid ? InvoiceEvent.parse(raw) : null;容易忽略的细节字段类型对了不代表语义对了——单位、时区、精度这些 schema 表达不了的东西只能靠字段命名和契约文档约定验证不了。容易踩错的 3 个细节坑一把z.coerce.boolean()当成字符串布尔解析错误写法z.coerce.boolean().parse(false)问题在哪它走 truthy/falsy 语义和Boolean(false)一样任何非空字符串都会变成true正确写法z.coerce.boolean().parse(false); // true非空字符串按 truthy 处理 // 正确写法显式映射语义不猜 z.preprocess((v) v true || v 1, z.boolean())坑二.optional()和.default()混用错误写法想让缺省时给个值却写了nickname: z.string().optional()问题在哪optional()只是放行undefined输出里该字段依然是undefined下游还得处理正确写法z.string().default(匿名用户)缺省时填值输出类型变为string坑三每次调用都重建 schema 实例错误写法在 handler 函数体里const s z.object({...}); return s.parse(input)问题在哪schema 是不可变、可复用的验证器每次重建既浪费又失去了多处共享同一实例的机会正确写法schema 提升到模块顶层的const想 parse 多少次都行什么时候用、什么时候不必用判断标准只有一条数据是否跨越了信任边界。必用浏览器输入、第三方或自家 API 的响应、消息队列事件、跨团队共享契约。只要数据从外面来就在边界上验一次。可跳过纯内部展示数据已被上游验证过、只在可信模块间流转。重复验证只消耗 CPU 和错误处理成本不增加安全性。体积敏感的场景可以看轻量版zod/mini函数式 APIz.parse(schema, data)更精简适合纯解析需要 transforms、locales 等完整能力时再用标准版。继续深入生态集成一行带过React Hook Formhookform/resolvers/zod把issues直接映射成字段级错误tRPC.input()/.output()直接接收 Zod schema前后端共用类型Drizzledrizzle-zod 从表定义生成 Zod schema仓库里有集成 fixturepackages/integration/fixtures/drizzle-zodJSON Schema内置toJSONSchema()接第三方系统时可以直接转换仓库内可以顺着读packages/zod/README.md功能清单与安装方式packages/docs/content/api.mdxAPI 参考packages/zod/src/v4/core/schemas.ts核心 schema 实现一句话带走编译期类型只守一半运行时数据过信任边界的地方都该让 schema 再查一遍。【免费下载链接】zodTypeScript-first schema validation with static type inference项目地址: https://gitcode.com/GitHub_Trending/zo/zod创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表