Zod 4 - A Evolução da Validação de Schemas TypeScript

2024.12.24

O que é Zod

Zod é uma biblioteca de declaração de schemas e validação TypeScript-first. Apenas definindo um schema, você pode realizar validação e inferência de tipos simultaneamente.

Novas Funcionalidades do Zod 4

Melhorias de Performance

Velocidade de validação:
- Zod 3: 100,000 ops/sec
- Zod 4: 300,000 ops/sec (3x mais rápido)

Velocidade de parsing:
- Até 5x mais rápido em schemas complexos

Nova API de Metadados

import { z } from 'zod';

const userSchema = z.object({
  name: z.string().describe('Nome do usuário'),
  email: z.string().email().describe('Endereço de email'),
  age: z.number().min(0).max(150).describe('Idade')
}).describe('Informações do usuário');

// Obter metadados
const metadata = userSchema.description;
const fields = userSchema.shape;

Uso Básico

Definição de Schema

import { z } from 'zod';

// Tipos primitivos
const stringSchema = z.string();
const numberSchema = z.number();
const booleanSchema = z.boolean();
const dateSchema = z.date();

// Objeto
const userSchema = z.object({
  id: z.number(),
  name: z.string(),
  email: z.string().email(),
  age: z.number().int().positive().optional(),
  role: z.enum(['user', 'admin']),
  createdAt: z.date().default(() => new Date())
});

// Inferência de tipo
type User = z.infer<typeof userSchema>;
// {
//   id: number;
//   name: string;
//   email: string;
//   age?: number;
//   role: 'user' | 'admin';
//   createdAt: Date;
// }

Validação

// parse: lança exceção em caso de falha
const user = userSchema.parse({
  id: 1,
  name: 'Alice',
  email: 'alice@example.com',
  role: 'user'
});

// safeParse: retorna resultado como objeto
const result = userSchema.safeParse(input);
if (result.success) {
  console.log(result.data);
} else {
  console.error(result.error.issues);
}

Transform

const schema = z.string()
  .transform(val => val.toLowerCase())
  .transform(val => val.trim());

const cleanSchema = z.object({
  price: z.string().transform(val => parseFloat(val)),
  quantity: z.string().transform(val => parseInt(val, 10))
});

Funcionalidades Avançadas

Refinement

const passwordSchema = z.string()
  .min(8, 'A senha deve ter pelo menos 8 caracteres')
  .refine(
    (val) => /[A-Z]/.test(val),
    { message: 'Inclua pelo menos uma letra maiúscula' }
  )
  .refine(
    (val) => /[0-9]/.test(val),
    { message: 'Inclua pelo menos um número' }
  );

// superRefine (retorna múltiplos erros)
const formSchema = z.object({
  password: z.string(),
  confirmPassword: z.string()
}).superRefine((data, ctx) => {
  if (data.password !== data.confirmPassword) {
    ctx.addIssue({
      code: z.ZodIssueCode.custom,
      message: 'As senhas não coincidem',
      path: ['confirmPassword']
    });
  }
});

Union e Discriminator

// Union normal
const responseSchema = z.union([
  z.object({ status: z.literal('success'), data: z.any() }),
  z.object({ status: z.literal('error'), message: z.string() })
]);

// Discriminated union (mais rápido)
const eventSchema = z.discriminatedUnion('type', [
  z.object({ type: z.literal('click'), x: z.number(), y: z.number() }),
  z.object({ type: z.literal('scroll'), offset: z.number() }),
  z.object({ type: z.literal('keypress'), key: z.string() })
]);

Schema Recursivo

interface Category {
  name: string;
  subcategories: Category[];
}

const categorySchema: z.ZodType<Category> = z.lazy(() =>
  z.object({
    name: z.string(),
    subcategories: z.array(categorySchema)
  })
);

Partial e Required

const userSchema = z.object({
  id: z.number(),
  name: z.string(),
  email: z.string()
});

// Tornar todos opcionais
const partialUser = userSchema.partial();

// Tornar apenas campos específicos opcionais
const updateUser = userSchema.partial({
  name: true,
  email: true
});

// Tornar opcionais obrigatórios
const requiredUser = userSchema.required();

Validação de API

Express

import express from 'express';
import { z } from 'zod';

const createUserSchema = z.object({
  body: z.object({
    name: z.string(),
    email: z.string().email()
  }),
  query: z.object({
    notify: z.string().optional()
  })
});

function validate<T extends z.ZodType>(schema: T) {
  return (req: Request, res: Response, next: NextFunction) => {
    const result = schema.safeParse(req);
    if (!result.success) {
      return res.status(400).json({ errors: result.error.issues });
    }
    next();
  };
}

app.post('/users', validate(createUserSchema), (req, res) => {
  // Validado
});

tRPC

import { z } from 'zod';
import { publicProcedure, router } from './trpc';

export const appRouter = router({
  createUser: publicProcedure
    .input(z.object({
      name: z.string().min(1),
      email: z.string().email()
    }))
    .mutation(async ({ input }) => {
      return db.user.create({ data: input });
    })
});

Integração com Bibliotecas de Formulário

React Hook Form

import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';

const schema = z.object({
  name: z.string().min(1, 'Nome é obrigatório'),
  email: z.string().email('Por favor, insira um endereço de email válido')
});

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

  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <input {...register('name')} />
      {errors.name && <span>{errors.name.message}</span>}
      {/* ... */}
    </form>
  );
}

Resumo

O Zod 4 tornou a validação de schemas TypeScript ainda mais rápida e fácil de usar. Com integração de inferência de tipos, funcionalidades ricas de validação e compatibilidade com frameworks, é uma ferramenta indispensável para o desenvolvimento de aplicações type-safe.

← Voltar para a lista