2.7 KiB
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