Files
gruperly/.agents/skills/zod/references/schema-use-enums.md
2026-09-04 16:49:24 -03:00

2.7 KiB

title, impact, impactDescription, tags
title impact impactDescription tags
Use Enums for Fixed String Values CRITICAL Plain strings accept any value including typos; enums restrict to valid values and enable autocomplete schema, enum, literal, union

Use Enums for Fixed String Values

When a field should only accept specific values (status, role, type), use z.enum() or z.literal() instead of z.string(). Plain strings accept any value including typos, while enums provide validation, type safety, and IDE autocomplete.

Incorrect (plain string for fixed values):

import { z } from 'zod'

const orderSchema = z.object({
  id: z.string(),
  status: z.string(),  // Accepts any string
  priority: z.string(),  // No constraints
})

type Order = z.infer<typeof orderSchema>
// { id: string; status: string; priority: string }

// Typos and invalid values pass validation
orderSchema.parse({
  id: '123',
  status: 'pendng',  // Typo passes
  priority: 'super-urgent',  // Invalid value passes
})

function processOrder(order: Order) {
  if (order.status === 'pending') {  // Might never match due to typos
    // ...
  }
}

Correct (using z.enum):

import { z } from 'zod'

const OrderStatus = z.enum(['pending', 'processing', 'shipped', 'delivered'])
const Priority = z.enum(['low', 'medium', 'high'])

const orderSchema = z.object({
  id: z.string(),
  status: OrderStatus,
  priority: Priority,
})

type Order = z.infer<typeof orderSchema>
// { id: string; status: 'pending' | 'processing' | 'shipped' | 'delivered'; priority: 'low' | 'medium' | 'high' }

// Typos are caught at validation
orderSchema.parse({
  id: '123',
  status: 'pendng',  // ZodError: Invalid enum value
  priority: 'super-urgent',  // ZodError: Invalid enum value
})

// Extract enum values for reuse
OrderStatus.options  // ['pending', 'processing', 'shipped', 'delivered']
type OrderStatusType = z.infer<typeof OrderStatus>  // 'pending' | 'processing' | ...

For native TypeScript enums:

enum Role {
  Admin = 'admin',
  User = 'user',
  Guest = 'guest',
}

// Use z.nativeEnum for TS enums
const userSchema = z.object({
  role: z.nativeEnum(Role),
})

For single literal values (discriminated unions):

const successResponse = z.object({
  status: z.literal('success'),
  data: z.unknown(),
})

const errorResponse = z.object({
  status: z.literal('error'),
  message: z.string(),
})

const response = z.discriminatedUnion('status', [
  successResponse,
  errorResponse,
])

When NOT to use this pattern:

  • When the set of valid values is dynamic or user-defined
  • When values come from a database that may have more options

Reference: Zod API - Enums