Validate code for Zod v4 compatibility by detecting deprecated APIs and ensuring adherence to v4 patterns
This skill helps identify and fix deprecated Zod v3 patterns that are incompatible with v4, ensuring code uses current APIs and avoiding future breaking changes.
Zod v4 introduced breaking changes that make v3 code non-functional or deprecated:
Code using deprecated patterns will break in future Zod releases.
const emailSchema = z.string().email();
const uuidSchema = z.string().uuid();
const datetimeSchema = z.string().datetime();
const urlSchema = z.string().url();
Correct v4 pattern:
const emailSchema = z.email();
const uuidSchema = z.uuid();
const datetimeSchema = z.iso.datetime();
const urlSchema = z.url();
Detection pattern:
grep -n "z\.string()\.email(" file.ts
grep -n "z\.string()\.uuid(" file.ts
grep -n "z\.string()\.datetime(" file.ts
Anti-pattern:
z.string({ message: "Required" });
z.string({ invalid_type_error: "Must be string" });
z.string({ required_error: "Field required" });
z.object({}, { errorMap: customErrorMap });
Correct v4 pattern:
z.string({ error: "Required" });
z.string({ error: "Must be string" });
z.string({ error: "Field required" });
z.object({}, { error: customErrorMap });
Detection pattern:
grep -n "message:" file.ts | grep -v "error:"
grep -n "invalid_type_error:" file.ts
grep -n "required_error:" file.ts
grep -n "errorMap:" file.ts
Anti-pattern:
const combined = schemaA.merge(schemaB);
Correct v4 pattern:
const combined = schemaA.extend(schemaB.shape);
Detection pattern:
grep -n "\.merge(" file.ts
Anti-pattern:
const trimmed = input.trim();
const validated = z.string().parse(trimmed);
Correct v4 pattern:
const validated = z.string().trim().parse(input);
Detection pattern: Check for manual trim/toLowerCase/toUpperCase before validation.
Run pattern matching across TypeScript files:
find . -name "*.ts" -o -name "*.tsx" | while read file; do
if grep -q "from ['\"]zod['\"]" "$file"; then
echo "Checking: $file"
grep -n "z\.string()\.email(" "$file" && echo " ❌ Use z.email()"
grep -n "z\.string()\.uuid(" "$file" && echo " ❌ Use z.uuid()"
grep -n "z\.string()\.datetime(" "$file" && echo " ❌ Use z.iso.datetime()"
grep -n "\.merge(" "$file" && echo " ❌ Use .extend()"
grep -n "message:" "$file" | grep -v "error:" && echo " ❌ Use error parameter"
fi
done
Ensure Zod v4 installed:
grep "\"zod\"" package.json
Should show "zod": "^4.0.0" or higher.
Verify type extraction uses correct syntax:
type User = z.infer<typeof userSchema>;
type UserInput = z.input<typeof userSchema>;
type UserOutput = z.output<typeof userSchema>;
Check safeParse pattern usage:
const result = schema.safeParse(data);
if (!result.success) {
console.error(result.error.flatten());
return;
}
const validData = result.data;
| v3 Pattern | v4 Pattern |
|---|---|
z.string().email() | z.email() |
z.string().uuid() | z.uuid() |
z.string().datetime() | z.iso.datetime() |
z.string().url() | z.url() |
z.string().ipv4() | z.ipv4() |
z.string().jwt() | z.jwt() |
z.string().base64() | z.base64() |
| v3 Pattern | v4 Pattern |
|---|---|
{ message: "..." } | { error: "..." } |
{ invalid_type_error: "..." } | { error: "..." } |
{ required_error: "..." } | { error: "..." } |
{ errorMap: fn } | { error: fn } |
| v3 Pattern | v4 Pattern |
|---|---|
a.merge(b) | a.extend(b.shape) |
z.intersection(a, b) | a.and(b) |
z.union([a, b]) | z.union([a, b]) (unchanged) |
Find and replace:
sed -i '' 's/z\.string()\.email()/z.email()/g' file.ts
sed -i '' 's/z\.string()\.uuid()/z.uuid()/g' file.ts
sed -i '' 's/z\.string()\.datetime()/z.iso.datetime()/g' file.ts
Replace error customization:
sed -i '' 's/message:/error:/g' file.ts
sed -i '' 's/invalid_type_error:/error:/g' file.ts
sed -i '' 's/required_error:/error:/g' file.ts
sed -i '' 's/\.merge(/\.extend(/g' file.ts
Run tests after migration:
npm test
This skill can be invoked by the review plugin when reviewing Zod-related code:
/review zod-compatibility
The skill will check for all deprecated patterns and provide remediation guidance.
Cross-Plugin References:
error parameterextend instead of merge