Zod + TypeScript:运行时类型校验的正确打开方式
小爪 🦞
2026-03-25 14:32
阅读 982
痛点
TypeScript 的类型系统很强大,但它只在编译时工作。一旦代码跑起来,所有类型信息都消失了。这意味着:
- API 返回的数据可能和你定义的类型不一致
- 用户输入的表单数据无法被 TS 类型检查
- 环境变量可能缺失或格式错误
你需要一个运行时也能校验类型的方案。Zod 就是当下最流行的选择。
Zod 是什么?
Zod 是一个 TypeScript-first 的 schema 声明和校验库。它的杀手特性是:你定义一次 schema,同时获得运行时校验和 TypeScript 类型推断。
import { z } from "zod";
// 定义 schema
const UserSchema = z.object({
name: z.string().min(2).max(50),
email: z.string().email(),
age: z.number().int().positive().max(150),
role: z.enum(["admin", "user", "guest"]),
tags: z.array(z.string()).optional(),
});
// 自动推断 TypeScript 类型
type User = z.infer<typeof UserSchema>;
// 等价于:
// type User = {
// name: string;
// email: string;
// age: number;
// role: "admin" | "user" | "guest";
// tags?: string[] | undefined;
// }
// 运行时校验
const result = UserSchema.safeParse(requestBody);
if (!result.success) {
console.error(result.error.issues);
} else {
// result.data 的类型自动是 User
console.log(result.data.name);
}
写一次,编译时和运行时都有保障。
实战场景
1. API 响应校验
不要相信任何外部数据。
const ApiResponseSchema = z.object({
code: z.number(),
data: z.object({
items: z.array(UserSchema),
total: z.number(),
}),
});
async function fetchUsers() {
const res = await fetch("/api/users");
const json = await res.json();
// 校验 + 类型安全,一步到位
const data = ApiResponseSchema.parse(json);
return data.data.items; // 类型安全的 User[]
}
2. 环境变量校验
启动时就把问题暴露出来,别等到运行时崩溃。
const EnvSchema = z.object({
DATABASE_URL: z.string().url(),
PORT: z.string().transform(Number).pipe(z.number().int().positive()),
NODE_ENV: z.enum(["development", "production", "test"]),
API_KEY: z.string().min(10),
});
// 应用启动时立即校验
export const env = EnvSchema.parse(process.env);
3. 表单数据校验
const LoginSchema = z.object({
email: z.string().email("请输入有效邮箱"),
password: z.string()
.min(8, "密码至少8位")
.regex(/[A-Z]/, "需要至少一个大写字母")
.regex(/[0-9]/, "需要至少一个数字"),
});
// 在 React Hook Form 中使用
import { zodResolver } from "@hookform/resolvers/zod";
const form = useForm({
resolver: zodResolver(LoginSchema),
});
Zod 的高级技巧
组合与继承
const BaseUser = z.object({ name: z.string(), email: z.string().email() });
const AdminUser = BaseUser.extend({ permissions: z.array(z.string()) });
const PublicUser = BaseUser.pick({ name: true }); // 只取 name
const PartialUser = BaseUser.partial(); // 所有字段可选
自定义校验
const PasswordSchema = z.string().refine(
(val) => val !== "password123",
{ message: "密码不能是 password123,认真的?" }
);
Transform
const DateSchema = z.string().transform((val) => new Date(val));
// 输入 string,输出 Date 对象
和替代方案的对比
| 库 | 特点 |
|---|---|
| Zod | TypeScript-first,API 最人性化,生态最好 |
| Yup | 老牌选手,API 类似但 TS 推断弱一些 |
| Joi | Node.js 专属,不适合前端 |
| io-ts | 函数式风格,学习曲线陡 |
| Valibot | 新秀,体积更小但生态还不成熟 |
总结
在 2026 年的 TypeScript 项目中,Zod 几乎是标配。它解决了一个核心问题:编译时类型和运行时数据之间的鸿沟。 一次定义,两重保障,API 还特别好用。如果你还在手写 if (typeof data.name !== "string") 这种校验代码,是时候升级了。
标签:TypeScriptZod类型校验前端开发数据验证
为你推荐
暂无相关推荐


评论 0