一键导入
zod4
Zod 4 syntax reference and migration guide. Use when writing Zod schemas to ensure correct Zod 4 patterns are used instead of deprecated Zod 3 syntax.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Zod 4 syntax reference and migration guide. Use when writing Zod schemas to ensure correct Zod 4 patterns are used instead of deprecated Zod 3 syntax.
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
| name | zod4 |
| description | Zod 4 syntax reference and migration guide. Use when writing Zod schemas to ensure correct Zod 4 patterns are used instead of deprecated Zod 3 syntax. |
This skill documents Zod 4 breaking changes and new patterns. Always use these patterns when writing Zod schemas in this codebase.
Format methods moved from z.string() chain to top-level z namespace:
| Zod 3 (Deprecated) | Zod 4 (Correct) |
|---|---|
z.string().email() | z.email() |
z.string().uuid() | z.uuid() |
z.string().url() | z.url() |
z.string().ipv4() | z.ipv4() |
z.string().ipv6() | z.ipv6() |
z.string().cidr() | z.cidrv4() / z.cidrv6() |
z.string().emoji() | z.emoji() |
z.string().base64() | z.base64() |
z.string().base64url() | z.base64url() |
z.string().nanoid() | z.nanoid() |
z.string().cuid() | z.cuid() |
z.string().cuid2() | z.cuid2() |
z.string().ulid() | z.ulid() |
// Zod 3 - DEPRECATED
const schema = z.object({
email: z.string().email(),
id: z.string().uuid(),
website: z.string().url(),
});
// Zod 4 - CORRECT
const schema = z.object({
email: z.email(),
id: z.uuid(),
website: z.url(),
});
z.uuid() is now RFC 9562/4122 compliant (stricter). Use z.guid() for permissive GUID matching.
error ParameterThe message, required_error, and invalid_type_error parameters are replaced with a unified error parameter:
// Zod 3 - DEPRECATED
z.string({ required_error: "Required", invalid_type_error: "Must be string" });
z.string().min(5, { message: "Too short" });
// Zod 4 - CORRECT
z.string({ error: "Must be a string" });
z.string().min(5, { error: "Too short" });
// For distinguishing missing vs wrong type:
z.string({
error: (issue) => (issue.input === undefined ? "Required" : "Must be string"),
});
// Zod 3 - DEPRECATED
z.string({ errorMap: (issue, ctx) => ({ message: "Custom error" }) });
// Zod 4 - CORRECT (return string directly, not { message })
z.string({ error: (issue) => "Custom error" });
Chainable object modifiers replaced with dedicated constructors:
| Zod 3 (Deprecated) | Zod 4 (Correct) |
|---|---|
z.object({}).strict() | z.strictObject({}) |
z.object({}).passthrough() | z.looseObject({}) |
z.object({}).strip() | z.object({}) (default) |
z.object({}).nonstrict() | Removed |
schema1.merge(schema2) | schema1.extend(schema2.shape) |
z.object({}).deepPartial() | Removed |
// Zod 3 - DEPRECATED
const strictSchema = z.object({ name: z.string() }).strict();
const looseSchema = z.object({ name: z.string() }).passthrough();
// Zod 4 - CORRECT
const strictSchema = z.strictObject({ name: z.string() });
const looseSchema = z.looseObject({ name: z.string() });
Single-argument usage removed. Must specify both key and value schemas:
// Zod 3 - DEPRECATED
z.record(z.string()); // Record<string, string>
// Zod 4 - CORRECT
z.record(z.string(), z.string()); // Record<string, string>
When using enum keys, all keys are required (exhaustive). Use z.partialRecord() for optional keys:
const Status = z.enum(["active", "inactive"]);
// All keys required
z.record(Status, z.number()); // { active: number, inactive: number }
// Keys optional
z.partialRecord(Status, z.number()); // { active?: number, inactive?: number }
POSITIVE_INFINITY, NEGATIVE_INFINITY) now rejected.safe() behaves like .int() (no longer accepts floats).int() validates only safe integers (Number.MIN_SAFE_INTEGER to Number.MAX_SAFE_INTEGER).nonempty() now behaves like .min(1) and returns string[] not [string, ...string[]].
For tuple type with at least one element:
// Zod 3 - returned [string, ...string[]]
z.array(z.string()).nonempty();
// Zod 4 - for non-empty tuple type
z.tuple([z.string()], z.string()); // [string, ...string[]]
.default() now short-circuits parsing. Default must match OUTPUT type, not input:
// Zod 3 behavior - default applied before parsing
z.string()
.transform((s) => s.length)
.default("hello"); // Worked
// Zod 4 - default must match output type
z.string()
.transform((s) => s.length)
.default(5); // Must be number
// Use .prefault() for old behavior (apply before parsing)
z.string()
.transform((s) => s.length)
.prefault("hello");
Defaults now applied automatically in optional fields:
const schema = z.object({
name: z.string().default("anonymous").optional(),
});
schema.parse({}); // { name: "anonymous" } - not { name: undefined }
z.enum() now handles native enums directly:
enum Status {
Active = "active",
Inactive = "inactive",
}
// Zod 3 - DEPRECATED
z.nativeEnum(Status);
// Zod 4 - CORRECT
z.enum(Status);
Also removed: .Enum and .Values properties. Use .enum only.
Await promises before parsing instead:
// Zod 3 - DEPRECATED
z.promise(z.string());
// Zod 4 - CORRECT
const result = await promise;
z.string().parse(result);
No longer a Zod schema. Uses input/output properties:
// Zod 3 - DEPRECATED
const fn = z.function().args(z.string()).returns(z.number());
// Zod 4 - CORRECT
const fn = z.function({
input: z.tuple([z.string()]),
output: z.number(),
});
// Implementation
const myFn = fn.implement((arg) => arg.length);
// Async implementation
const myAsyncFn = fn.implementAsync(async (arg) => arg.length);
ctx.path removed for performance// Zod 3 - type narrowing worked
z.string().refine((s): s is "hello" => s === "hello");
// Zod 4 - no type narrowing, use transform instead
z.string().transform((s) => {
if (s !== "hello") throw new Error("Must be hello");
return s as "hello";
});
Error classinstanceof Error checks will fail.format(), .flatten(), .formErrors, .addIssue(), .addIssues()z.treeifyError() for error formatting// Zod 4 error handling
try {
schema.parse(data);
} catch (e) {
if (e instanceof z.ZodError) {
const formatted = z.treeifyError(e);
console.log(formatted);
}
}
| Removed | Replacement |
|---|---|
z.ostring(), z.onumber() | z.string().optional() |
z.literal(Symbol()) | Not supported |
ZodType.create() | Direct instantiation |
z.ZodBranded | Use branded types differently |
z.preprocess() | Use .transform() or .pipe() |
._def moved to ._zod.defZodType<Output, Input> (removed Def parameter)z.core namespace for shared utilitiesFor datetime strings, use:
z.iso.datetime(); // ISO 8601 datetime string
z.iso.date(); // ISO 8601 date string
z.iso.time(); // ISO 8601 time string
Generate git commit messages, splitting work into granular commits by concern. Use when user says "commit this", "write a commit", "commit message", or invokes /commit.
Write PostgreSQL trigger functions and migrations for this Supabase project. Enforces all security and performance rules derived from the project's migration history to avoid Supabase advisor warnings (lints 0011, 0028, 0029).
Perform thorough, structured code review on a PR. Detects stack/conventions from the codebase, checks linked issues for acceptance criteria, then reviews the diff with severity-graded findings.
Methodically work through PR review comments one-by-one. Fetches comments via gh CLI, explains each reviewer comment, assesses its validity, proposes a solution, waits for user approval, implements, then moves to the next comment.
This skill should be used when the user asks "how does this work", "what patterns are used here", "show me how this is done in the codebase", or before implementing features to understand existing conventions. Explores codebase patterns for types, components, validation, data fetching, and file organization without making any changes.
This skill should be used when the user reports a bug, asks to "fix this bug", "debug this issue", "something is broken", "this isn't working", or requests investigation of unexpected behavior. Enforces minimal scope changes, prevents touching working code, and includes build verification steps.