Zod 一个 TypeScript 优先的模式声明和验证库

Date
Created
Mar 31, 2025 02:58 AM
Descrption
好记性不如烂笔头
Tags
前端工程化
记录
三方库
notion image
Zod 是一个用于 TypeScriptJavaScript模式(Schema)定义与数据验证库,比传统的 JoiYup 更轻量、类型安全,并且能与 TypeScript 完美集成
它可以:
✅ 定义数据结构
✅ 验证用户输入
✅ 进行数据解析与转换
✅ 结合 TypeScript 提供类型推导
 

1. 安装 Zod:

在你的项目中安装 Zod:
yarn add zod
 

2. 基本使用:

Zod 的核心是 Schema(模式定义),用它来校验数据是否合法。

2.1 定义 Schema

import { z } from "zod"; // 定义一个用户 Schema const UserSchema = z.object({ name: z.string(), age: z.number(), email: z.string().email(), // 验证邮箱格式 }); // 传入数据进行验证 const user = { name: "Alice", age: 25, email: "alice@example.com" }; const result = UserSchema.safeParse(user); console.log(result);
safeParse(data) 会返回 { success: true, data: validatedData }{ success: false, error: errorDetails }
parse(data) 直接返回验证后的数据,如果失败会抛出异常
 

3. 进阶用法:

 

3.1 Schema 组合

Zod 允许你组合多个 Schema:
// 定义一个用户 Schema const UserSchema = z.object({ name: z.string(), age: z.number(), email: z.string().email(), // 验证邮箱格式 }); // 定义一个地址 Schema const AddressSchema = z.object({ city: z.string(), zip: z.string().length(5), // 必须是 5 位字符串 }); //extend const UserWithAddressSchema = UserSchema.extend({ address: AddressSchema, }); const userWithAddress = { name: "Alice", age: 25, email: "alice@example.com", address: { city: "New York", zip: "10001" }, }; console.log(UserWithAddressSchema.safeParse(userWithAddress));
extend({}) 允许你扩展已有的 Schema。
 

3.2 可选字段 & 默认值

const UserSchema = z.object({ name: z.string(), age: z.number().optional(), // `age` 是可选的 role: z.string().default("user"), // 设定默认值 }); console.log(UserSchema.parse({ name: "Alice" })); // 输出:{ name: "Alice", role: "user" }

3.3 自定义错误消息

const PasswordSchema = z.string().min(6, { message: "密码至少 6 位" }); const result = PasswordSchema.safeParse("123"); console.log(result.error?.format()); // 自定义错误消息

3.4 处理数组 & 联合类型

const NumbersArray = z.array(z.number()); const response = NumbersArray.safeParse([1, 2, "3"]); console.log(response.error); // 会报错,因为 "3" 不是数字
const StatusSchema = z.union([z.literal("active"), z.literal("inactive")]); console.log(StatusSchema.safeParse("active")); // ✅ console.log(StatusSchema.safeParse("disabled")); // ❌

4. Zod 与 TypeScript 集成

Zod 允许你从 Schema 推导 TypeScript 类型
const UserSchema = z.object({ name: z.string(), age: z.number(), }); type User = z.infer<typeof UserSchema>; const user: User = { name: "Alice", age: 30 }; // ✅
z.infer<typeof Schema> 自动推导 TypeScript 类型,避免手动定义。

5. 高级用法

5.1 处理嵌套数据

const BlogPostSchema = z.object({ title: z.string(), author: z.object({ name: z.string(), email: z.string().email(), }), }); const post = { title: "My first post", author: { name: "John", email: "john@example.com" }, }; console.log(BlogPostSchema.safeParse(post)); // ✅

5.2 解析 URL、日期

const URLSchema = z.string().url(); console.log(URLSchema.safeParse("https://example.com")); // ✅ const DateSchema = z.preprocess((arg) => new Date(arg as string), z.date()); console.log(DateSchema.safeParse("2024-03-31")); // ✅

5.3 结合 Express / Next.js API

Next.js API 路由 中用 Zod 进行参数验证:
import { z } from "zod"; import { NextApiRequest, NextApiResponse } from "next"; const RequestSchema = z.object({ name: z.string(), age: z.number().min(18, "年龄必须大于 18"), }); export default function handler(req: NextApiRequest, res: NextApiResponse) { const validation = RequestSchema.safeParse(req.body); if (!validation.success) { return res.status(400).json({ error: validation.error.format() }); } return res.status(200).json({ message: "成功" }); }

6. 总结

Zod 更安全、类型友好,适合 TypeScript
支持 Schema 组合、默认值、嵌套对象、数组
适用于 API、表单验证,甚至数据库 Schema 校验
性能优秀,比 Yup 和 Joi 更轻量
 

举几个例子:

登录输入校验

'use server' import { z } from 'zod' import { createUser, getUser } from '@/lib/db/user' import { signIn } from '@/auth' const authFormSchema = z.object({ email: z.string().email(), password: z.string().min(6), }) export interface LoginActionState { status: 'idle' | 'in_progress' | 'success' | 'failed' | 'invalid_data' } export const login = async ( _: LoginActionState, formData: FormData ): Promise<LoginActionState> => { try { console.log('formData', formData) const validatedData = authFormSchema.parse({ email: formData.get('email'), password: formData.get('password'), }) console.log('validatedData', validatedData) const result = await signIn('credentials', { email: validatedData.email, password: validatedData.password, redirect: false, }) console.log('result', result) return { status: 'success' } } catch (error) { if (error instanceof z.ZodError) { return { status: 'invalid_data' } } return { status: 'failed' } } } export interface RegisterActionState { status: | 'idle' | 'in_progress' | 'success' | 'failed' | 'user_exists' | 'invalid_data' } export const register = async ( _: RegisterActionState, formData: FormData ): Promise<RegisterActionState> => { try { const validatedData = authFormSchema.parse({ email: formData.get('email'), password: formData.get('password'), }) const [user] = await getUser(validatedData.email) if (user) { return { status: 'user_exists' } as RegisterActionState } await createUser(validatedData.email, validatedData.password) await signIn('credentials', { email: validatedData.email, password: validatedData.password, redirect: false, }) return { status: 'success' } } catch (error) { if (error instanceof z.ZodError) { return { status: 'invalid_data' } } return { status: 'failed' } } }