TypeScript 的类型在编译后完全消失,运行时 incoming 数据(API 请求、表单输入、环境变量)可能是任意值。Zod 是在 TypeScript 生态中最流行的运行时类型验证库——它与 TS 类型系统天然配合,用声明式的方式定义数据schema,并自动推断出对应的 TS 类型。
一、Zod 基础
npm install zod
import { z } from 'zod';
// 定义 schema
const UserSchema = z.object({
id: z.number(),
name: z.string().min(1).max(100),
email: z.string().email(),
age: z.number().int().min(0).optional(),
role: z.enum(['user', 'admin']),
});
// 自动推断 TypeScript 类型
type User = z.infer<typeof UserSchema>;
// 等同于:
// type User = { id: number; name: string; email: string; age?: number; role: 'user' | 'admin' }
// 运行时验证
const result = UserSchema.safeParse(unknownData);
if (!result.success) {
console.log(result.error.issues); // 详细的验证错误
} else {
const user: User = result.data;
}
二、API 请求验证
// Express 中间件
import { Request, Response, NextFunction } from 'express';
import { z, ZodError } from 'zod';
const CreatePostSchema = z.object({
title: z.string().min(1).max(200),
content: z.string().min(1),
tags: z.array(z.string()).max(10),
});
function validateBody<T extends z.ZodType>(schema: T) {
return (req: Request, res: Response, next: NextFunction) => {
const result = schema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({
error: 'Validation failed',
details: result.error.issues,
});
}
req.body = result.data;
next();
};
}
app.post('/posts', validateBody(CreatePostSchema), (req, res) => {
// req.body 已验证为 CreatePost 类型
const { title, content, tags } = req.body;
// ...
});
三、环境变量类型安全
// env.ts
import { z } from 'zod';
const envSchema = z.object({
NODE_ENV: z.enum(['development', 'production', 'test']),
PORT: z.string().transform(Number).default('3000'),
DATABASE_URL: z.string().url(),
JWT_SECRET: z.string().min(32),
REDIS_URL: z.string().url().optional(),
});
export const env = envSchema.parse(process.env);
// 如果缺少必需变量或格式错误,启动时直接抛出清晰的错误
四、React Hook Form + Zod
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
const schema = z.object({
email: z.string().email('请输入有效的邮箱'),
password: z.string().min(8, '密码至少8位'),
});
type FormData = z.infer<typeof schema>;
function LoginForm() {
const { register, handleSubmit, formState: { errors } } = useForm<FormData>({
resolver: zodResolver(schema),
});
return (
<form onSubmit={handleSubmit((data) => console.log(data))}>
<input {...register('email')} />
{errors.email && <span>{errors.email.message}</span>}
<input type="password" {...register('password')} />
<button type="submit">登录</button>
</form>
);
}
相关阅读
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。