Zod TypeScript schema validation library for runtime type checking. USE WHEN: user mentions "Zod", "schema validation", "form validation", "API validation", "type-safe validation", asks about "runtime validation", "React Hook Form validation" DO NOT USE FOR: Yup projects (use yup skill), class-validator/NestJS DTOs (use class-validator skill), PropTypes, compile-time only validation

GitHub
安装命令
npx skhub add claude-dev-suite/zod
Markdown
SKILL.md

Zod - Quick Reference

When to Use This Skill

  • Form validation with React Hook Form
  • API data parsing/validation
  • Type inference from schemas

Deep Knowledge: Use mcp__documentation__fetch_docs with technology: zod for comprehensive documentation.

Essential Patterns

Basic Schema

import { z } from 'zod';

const userSchema = z.object({
  name: z.string().min(2),
  email: z.string().email(),
  age: z.number().min(18).optional(),
  role: z.enum(['admin', 'user'])
});

// Type inference
type User = z.infer<typeof userSchema>;

// Parse
const user = userSchema.parse(data); // throws ZodError
const result = userSchema.safeParse(data); // { success, data/error }

Common Validations

// String
z.string().min(1).max(100).email().url().uuid()

// Number
z.number().int().positive().min(0).max(100)

// Custom validation
z.string().refine(val => val.startsWith('@'), "Must start with @")

// Object cross-field
z.object({
  password: z.string(),
  confirm: z.string()
}).refine(d => d.password === d.confirm, {
  message: "Passwords don't match",
  path: ['confirm']
});

React Hook Form

import { zodResolver } from '@hookform/resolvers/zod';

const { register, handleSubmit, formState: { errors } } = useForm({
  resolver: zodResolver(userSchema)
});

<input {...register('name')} />
{errors.name && <span>{errors.name.message}</span>}

Schema Operations

userSchema.partial()           // All fields optional
userSchema.pick({ name: true }) // Pick fields
userSchema.omit({ id: true })   // Omit fields
userSchema.extend({ phone: z.string() }) // Extend

When NOT to Use This Skill

  • Yup existing projects - Use yup skill for Formik integration
  • NestJS DTOs - Use class-validator skill for decorator-based validation
  • Compile-time only checks - TypeScript types are sufficient
  • Simple PropTypes - React PropTypes might be enough

Anti-Patterns to Avoid

  • Do not define schemas inside components (performance)
  • Do not use .parse() for user input without try/catch
  • Do not forget custom error messages

Anti-Patterns

Anti-PatternWhy It's BadCorrect Approach
Schema inside componentsRe-created on every renderDefine at module level
Using .parse() without try-catchThrows unhandled errorsUse .safeParse() for user input
No custom error messagesGeneric "Invalid input"Add custom messages to all validations
Validating twice (FE + BE)Performance wasteShare schemas between client/server
Not using z.inferManual type definitionsAlways use type inference
Ignoring .transform()Miss data normalizationUse transforms for trimming, etc.

Quick Troubleshooting

IssueCauseSolution
"Expected string, received number"Type mismatchUse z.coerce.string() for type coercion
Performance issuesSchema in componentMove schema to module level
Async validation not workingUsing .parse()Use .parseAsync() for async refinements
Nested errors not showingNot using flatten()Use error.flatten() for nested structures
"Invalid type" on optional fieldsUsing undefinedUse .optional() or .nullable()
Form not validatingResolver not setAdd resolver: zodResolver(schema)

Production Readiness

Error Handling

import { z, ZodError } from 'zod';

// Safe parsing with error handling
function validateInput<T>(schema: z.ZodSchema<T>, data: unknown): T {
  const result = schema.safeParse(data);

  if (!result.success) {
    const errors = result.error.flatten();
    throw new ValidationError(errors);
  }

  return result.data;
}

// Custom error messages
const userSchema = z.object({
  email: z.string({
    required_error: 'Email is required',
    invalid_type_error: 'Email must be a string',
  }).email({ message: 'Invalid email format' }),

  password: z.string()
    .min(8, { message: 'Password must be at least 8 characters' })
    .regex(/[A-Z]/, { message: 'Password must contain uppercase letter' })
    .regex(/[0-9]/, { message: 'Password must contain number' }),
});

// Error formatting for API responses
function formatZodError(error: ZodError): Record<string, string[]> {
  return error.flatten().fieldErrors as Record<string, string[]>;
}

API Integration

// Express middleware
import { Request, Response, NextFunction } from 'express';

function validate<T extends z.ZodSchema>(schema: T) {
  return (req: Request, res: Response, next: NextFunction) => {
    const result = schema.safeParse(req.body);

    if (!result.success) {
      return res.status(400).json({
        code: 'VALIDATION_ERROR',
        errors: formatZodError(result.error),
      });
    }

    req.body = result.data;
    next();
  };
}

// Usage
app.post('/users', validate(createUserSchema), createUser);

// tRPC integration
export const appRouter = t.router({
  createUser: t.procedure
    .input(createUserSchema)
    .mutation(({ input }) => {
      // input is fully typed and validated
      return db.users.create({ data: input });
    }),
});

Complex Validations

// Conditional fields
const formSchema = z.discriminatedUnion('type', [
  z.object({
    type: z.literal('individual'),
    name: z.string(),
    ssn: z.string().regex(/^\d{3}-\d{2}-\d{4}$/),
  }),
  z.object({
    type: z.literal('business'),
    companyName: z.string(),
    ein: z.string().regex(/^\d{2}-\d{7}$/),
  }),
]);

// Async validation
const uniqueEmailSchema = z.string().email().refine(
  async (email) => {
    const exists = await db.users.findByEmail(email);
    return !exists;
  },
  { message: 'Email already registered' }
);

// Transform and validate
const dateSchema = z.string()
  .transform((str) => new Date(str))
  .refine((date) => !isNaN(date.getTime()), 'Invalid date');

// Preprocess for type coercion
const querySchema = z.object({
  page: z.preprocess(
    (val) => parseInt(val as string, 10),
    z.number().min(1).default(1)
  ),
  limit: z.preprocess(
    (val) => parseInt(val as string, 10),
    z.number().min(1).max(100).default(20)
  ),
});

Testing

import { describe, it, expect } from 'vitest';

describe('userSchema', () => {
  it('validates correct input', () => {
    const input = { email: 'test@example.com', password: 'Password1!' };
    const result = userSchema.safeParse(input);

    expect(result.success).toBe(true);
    if (result.success) {
      expect(result.data.email).toBe('test@example.com');
    }
  });

  it('fails on invalid email', () => {
    const input = { email: 'invalid', password: 'Password1!' };
    const result = userSchema.safeParse(input);

    expect(result.success).toBe(false);
    if (!result.success) {
      expect(result.error.flatten().fieldErrors.email).toContain('Invalid email format');
    }
  });

  it('fails on weak password', () => {
    const input = { email: 'test@example.com', password: 'weak' };
    const result = userSchema.safeParse(input);

    expect(result.success).toBe(false);
  });
});

// Property-based testing with fast-check
import * as fc from 'fast-check';

it('accepts all valid emails', () => {
  fc.assert(
    fc.property(fc.emailAddress(), (email) => {
      const result = z.string().email().safeParse(email);
      return result.success;
    })
  );
});

Performance

// Define schemas outside components/functions
const schemas = {
  user: z.object({ /* ... */ }),
  product: z.object({ /* ... */ }),
} as const;

// Lazy loading for circular references
const categorySchema: z.ZodType<Category> = z.lazy(() =>
  z.object({
    name: z.string(),
    children: z.array(categorySchema).optional(),
  })
);

// Coercion for better performance on known types
const numberSchema = z.coerce.number(); // Auto-converts strings
const dateSchema = z.coerce.date();

Monitoring Metrics

MetricTarget
Validation errorsTrack & analyze
Schema coverage100% API inputs
Type inferenceAll schemas
Parse time< 5ms

Checklist

  • Custom error messages
  • safeParse for user input
  • Error formatting for API
  • Schemas defined at module level
  • Type inference (z.infer)
  • Complex validation (refine)
  • Async validation where needed
  • Unit tests for schemas
  • Integration with React Hook Form
  • OpenAPI generation (zod-to-openapi)

Further Reading

For advanced validations: mcp__documentation__fetch_docs

  • Technology: zod (available in MCP)
  • Zod Docs
发现
标签

此技能尚未发布标签。

版本
最新版本元数据

版本

v2026.09.24

发布时间

Sep 24, 2026

分类

未分类

许可证

MIT

源路径

skills/validation/zod

默认分支

main

最新提交

9496306

Tree SHA

fe4e2f1