Added AI skills
This commit is contained in:
142
.agents/skills/zod/references/compose-preprocess.md
Normal file
142
.agents/skills/zod/references/compose-preprocess.md
Normal file
@@ -0,0 +1,142 @@
|
||||
---
|
||||
title: Use preprocess() for Data Normalization
|
||||
impact: MEDIUM
|
||||
impactDescription: Validating before cleaning data causes false rejections; preprocess() normalizes input before schema validation runs
|
||||
tags: compose, preprocess, normalize, cleaning
|
||||
---
|
||||
|
||||
## Use preprocess() for Data Normalization
|
||||
|
||||
When incoming data needs normalization before validation (trimming whitespace, parsing JSON strings, converting formats), use `z.preprocess()`. This runs a function on the raw input before Zod's type checking, allowing you to clean data that would otherwise fail validation.
|
||||
|
||||
**Incorrect (validation fails on unnormalized data):**
|
||||
|
||||
```typescript
|
||||
import { z } from 'zod'
|
||||
|
||||
const userSchema = z.object({
|
||||
name: z.string().min(1),
|
||||
email: z.string().email(),
|
||||
config: z.object({
|
||||
theme: z.string(),
|
||||
}),
|
||||
})
|
||||
|
||||
// Raw form data
|
||||
const formData = {
|
||||
name: ' John Doe ', // Has whitespace
|
||||
email: 'JOHN@EXAMPLE.COM', // Uppercase
|
||||
config: '{"theme": "dark"}', // JSON string, not object
|
||||
}
|
||||
|
||||
userSchema.parse(formData)
|
||||
// ZodError: Expected object, received string at "config"
|
||||
```
|
||||
|
||||
**Correct (using preprocess):**
|
||||
|
||||
```typescript
|
||||
import { z } from 'zod'
|
||||
|
||||
// Preprocess normalizes before validation
|
||||
const trimmedString = z.preprocess(
|
||||
(val) => (typeof val === 'string' ? val.trim() : val),
|
||||
z.string()
|
||||
)
|
||||
|
||||
const lowercaseEmail = z.preprocess(
|
||||
(val) => (typeof val === 'string' ? val.toLowerCase().trim() : val),
|
||||
z.string().email()
|
||||
)
|
||||
|
||||
const jsonObject = z.preprocess(
|
||||
(val) => {
|
||||
if (typeof val === 'string') {
|
||||
try {
|
||||
return JSON.parse(val)
|
||||
} catch {
|
||||
return val // Let Zod report the error
|
||||
}
|
||||
}
|
||||
return val
|
||||
},
|
||||
z.object({ theme: z.string() })
|
||||
)
|
||||
|
||||
const userSchema = z.object({
|
||||
name: trimmedString.pipe(z.string().min(1)),
|
||||
email: lowercaseEmail,
|
||||
config: jsonObject,
|
||||
})
|
||||
|
||||
const formData = {
|
||||
name: ' John Doe ',
|
||||
email: 'JOHN@EXAMPLE.COM',
|
||||
config: '{"theme": "dark"}',
|
||||
}
|
||||
|
||||
const user = userSchema.parse(formData)
|
||||
// { name: 'John Doe', email: 'john@example.com', config: { theme: 'dark' } }
|
||||
```
|
||||
|
||||
**Common preprocessing patterns:**
|
||||
|
||||
```typescript
|
||||
// Trim all strings
|
||||
const trimmedString = z.preprocess(
|
||||
(val) => (typeof val === 'string' ? val.trim() : val),
|
||||
z.string()
|
||||
)
|
||||
|
||||
// Parse numeric strings
|
||||
const numericString = z.preprocess(
|
||||
(val) => (typeof val === 'string' ? Number(val) : val),
|
||||
z.number()
|
||||
)
|
||||
|
||||
// Parse boolean-like values
|
||||
const booleanLike = z.preprocess(
|
||||
(val) => {
|
||||
if (val === 'true' || val === '1' || val === 1) return true
|
||||
if (val === 'false' || val === '0' || val === 0) return false
|
||||
return val
|
||||
},
|
||||
z.boolean()
|
||||
)
|
||||
|
||||
// Parse date strings
|
||||
const dateString = z.preprocess(
|
||||
(val) => (typeof val === 'string' ? new Date(val) : val),
|
||||
z.date()
|
||||
)
|
||||
|
||||
// Split comma-separated strings into arrays
|
||||
const csvArray = z.preprocess(
|
||||
(val) => (typeof val === 'string' ? val.split(',').map(s => s.trim()) : val),
|
||||
z.array(z.string())
|
||||
)
|
||||
```
|
||||
|
||||
**Preprocess vs Transform:**
|
||||
|
||||
```typescript
|
||||
// preprocess() runs BEFORE type checking
|
||||
// Use for: Normalizing input format before validation
|
||||
z.preprocess(val => String(val).trim(), z.string().min(1))
|
||||
|
||||
// transform() runs AFTER type checking
|
||||
// Use for: Converting validated data to different format
|
||||
z.string().transform(s => s.toUpperCase())
|
||||
|
||||
// Order of operations:
|
||||
// 1. preprocess receives raw unknown input
|
||||
// 2. Zod validates the preprocessed value
|
||||
// 3. transform converts the validated value
|
||||
```
|
||||
|
||||
**When NOT to use this pattern:**
|
||||
- When `.coerce` methods handle the conversion (simpler)
|
||||
- When transformation should happen after validation (use transform)
|
||||
- When normalization could hide validation errors
|
||||
|
||||
Reference: [Zod API - preprocess](https://zod.dev/api#preprocess)
|
||||
Reference in New Issue
Block a user