Zod schema validation
Skill findscripter/everything-skills/02-engineering/zod-schema-validation
当用 TypeScript 定义表单/API 入参、环境变量或运行时数据校验时使用;做用 Zod 定义 Schema 并产出经 safeParse 校验后的类型安全数据与可序列化错误;不适用于纯静态类型标注(用 interface)或非 TS 运行时校验;触发词:zod、schema 校验、z.infer、safeParse、表单校验、环境变量校验From its SKILL.md
npx -y skills add findscripter/everything-skills --skill zod-schema-validationAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its file declares
Copied from the file, not written here
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
6.6 KB, ~1.7k tokens by cl100k_base, as published. Nobody here has run it
何时使用
适合:
- 为表单或 API 入参定义 TypeScript 运行时校验 Schema。
- 校验环境变量(
process.env),做到启动即失败(fail-fast)。 - 集成 React Hook Form(
@hookform/resolvers/zod)。 - 从运行时 Schema 推导静态类型,避免手写重复
interface。 - 编写跨字段校验、异步校验等复杂规则。
- 转换输入数据(字符串转 Date / 数字、布尔归一化)。
- 统一错误信息格式以供前端消费。
不该用:
- 纯静态类型标注、无运行时校验需求时,直接写
interface/type即可,引入 Zod 是过度设计。 - 非 TypeScript 项目,或运行时校验已由后端框架(如管道、DTO 装饰器)统一处理时。
- 替代环境专属校验、测试或专家评审;缺少输入约束、权限或成功标准时应先澄清。
核心理念:Zod 让你只定义一次 Schema,静态类型由 z.infer 自动推导,消除「写 interface 又写校验」的重复。它本质是「解析(parse)而非仅校验」——parse/safeParse 返回干净且带类型的数据,并默认剥离未知字段。
步骤
- 定义 Schema:用
z.object组合z.string()/z.number()等原语;入参来自FormData/URLSearchParams时用z.coerce.*自动转型。 - 推导类型:
export type T = z.infer<typeof Schema>,全程复用,不再手写 interface。 - 校验数据:优先
safeParse,靠 TS 控制流收窄分支处理成功/失败,避免散落的 try/catch。 - 定制规则:单字段约束用
.min/.max/.regex带 message;跨字段或自定义逻辑用.refine/.superRefine,并务必传path把错误挂到正确字段。 - 输出错误:对
ZodError用.flatten()或.format()得到可序列化、人类可读的错误,交给前端。
指令
- 取数据后立即在边界处校验(API 入口、Server Action、表单 resolver),不要让未校验数据流入业务逻辑。
safeParse优先于parse;只有在「失败即应崩溃」的场景(如环境变量加载)才用parse。- 创建与更新需求不同时定义独立 Schema,不要只靠
.partial()复用。 - 递归结构用
z.lazy(() => NodeSchema),并显式声明基础类型,避免「类型实例化过深」报错。
示例
原语与类型推导:
import { z } from "zod";
const UserSchema = z.object({
id: z.string().uuid(),
username: z.string().min(3).max(20),
email: z.string().email(),
role: z.enum(["ADMIN", "USER", "GUEST"]).default("USER"),
age: z.number().min(18).optional(),
website: z.string().url().nullable(),
tags: z.array(z.string()).min(1),
});
// 直接从 Schema 推导类型,无需另写 interface User
export type User = z.infer<typeof UserSchema>;
safeParse 优于 parse(无需 try/catch,TS 自动收窄):
const schema = z.string().email();
const result = schema.safeParse("[email protected]");
if (!result.success) {
console.log(result.error.format()); // 失败分支
} else {
const validEmail = result.data; // 类型为 string
}
跨字段校验(注意 path):
const formSchema = z.object({
password: z.string().min(8),
confirmPassword: z.string(),
}).refine((data) => data.password === data.confirmPassword, {
message: "两次密码不一致",
path: ["confirmPassword"], // 把错误挂到具体字段
});
Next.js Server Action(FormData 必须 coerce):
"use server";
import { z } from "zod";
const createPostSchema = z.object({
title: z.string().min(3),
content: z.string().optional(),
published: z.coerce.boolean().default(false), // checkbox "on" -> true
});
export async function createPost(prevState: any, formData: FormData) {
const rawData = Object.fromEntries(formData.entries());
const validated = createPostSchema.safeParse(rawData);
if (!validated.success) {
return { errors: validated.error.flatten().fieldErrors };
}
const { title, content, published } = validated.data;
return { success: true };
}
环境变量 fail-fast:
const envSchema = z.object({
DATABASE_URL: z.string().url(),
NODE_ENV: z.enum(["development", "test", "production"]).default("development"),
PORT: z.coerce.number().default(3000),
API_KEY: z.string().min(10),
});
const env = envSchema.parse(process.env); // 缺失或非法立即报错
export default env;
React Hook Form:用 zodResolver(loginSchema) 接入 useForm,表单值类型用 z.infer<typeof loginSchema>,错误从 formState.errors 读取。
注意事项
z.coerce.boolean()的坑:它会把"false"/"off"等非空字符串转成true,需要时用自定义 preprocess 处理。- Schema 就近放在使用它的组件或 API 路由旁,保持关注点分离。
.optional()只允许undefined,不放过空字符串。若空串代表「无值」,用.or(z.literal(""))或z.string().transform(v => v === "" ? undefined : v).optional()。.transform()会改变推导出的类型(如string -> number),先 transform 再 refine 校验结果。- i18n 可用
z.setErrorMap设置全局自定义错误映射。 - 本技能仅适用于明确匹配上述范围的任务,不替代环境专属测试与专家评审。
互见
- React Hook Form 表单管理与提交流程。
- Next.js Server Actions / App Router 数据流。
- TypeScript 类型推导与控制流收窄实践。
采编自 sickn33/antigravity-awesome-skills(MIT)。
What ships with it
Read from the repository
Just SKILL.md. No reference files, no scripts.