Added AI skills
This commit is contained in:
105
.agents/skills/zod/references/refine-transform-coerce.md
Normal file
105
.agents/skills/zod/references/refine-transform-coerce.md
Normal file
@@ -0,0 +1,105 @@
|
||||
---
|
||||
title: Distinguish transform() from refine() and coerce()
|
||||
impact: MEDIUM
|
||||
impactDescription: Using wrong method causes validation to pass with wrong data; each method has distinct purpose
|
||||
tags: refine, transform, coerce, conversion
|
||||
---
|
||||
|
||||
## Distinguish transform() from refine() and coerce()
|
||||
|
||||
`.refine()` validates and returns boolean, `.transform()` converts data to new format, and `.coerce` converts input before validation. Using the wrong one causes bugs where validation passes but data is wrong.
|
||||
|
||||
**Purpose of each method:**
|
||||
|
||||
```typescript
|
||||
import { z } from 'zod'
|
||||
|
||||
// coerce: Convert type BEFORE validation
|
||||
// Input: unknown -> Output: validated type
|
||||
z.coerce.number().parse('42') // Converts "42" to 42, then validates as number
|
||||
|
||||
// refine: Validate with custom logic, return boolean
|
||||
// Input: T -> Output: T (unchanged, but validated)
|
||||
z.number().refine((n) => n > 0) // Validates n > 0, returns n unchanged
|
||||
|
||||
// transform: Convert to different type AFTER validation
|
||||
// Input: T -> Output: U (different type)
|
||||
z.string().transform((s) => s.length) // Validates string, returns length
|
||||
```
|
||||
|
||||
**Incorrect (using transform for validation):**
|
||||
|
||||
```typescript
|
||||
// Wrong: transform should convert, not validate
|
||||
const schema = z.number().transform((n) => {
|
||||
if (n < 0) throw new Error('Must be positive') // Don't throw in transform
|
||||
return n
|
||||
})
|
||||
```
|
||||
|
||||
**Correct (using appropriate method):**
|
||||
|
||||
```typescript
|
||||
import { z } from 'zod'
|
||||
|
||||
// VALIDATION: Use refine - returns boolean, data unchanged
|
||||
const positiveNumber = z.number().refine(
|
||||
(n) => n > 0,
|
||||
{ message: 'Must be positive' }
|
||||
)
|
||||
|
||||
positiveNumber.parse(5) // 5
|
||||
positiveNumber.parse(-1) // ZodError: Must be positive
|
||||
|
||||
// CONVERSION: Use transform - returns new value
|
||||
const stringLength = z.string().transform((s) => s.length)
|
||||
|
||||
type StringLength = z.infer<typeof stringLength> // number
|
||||
stringLength.parse('hello') // 5
|
||||
|
||||
// COERCION: Use coerce - converts input type
|
||||
const coercedNumber = z.coerce.number()
|
||||
|
||||
coercedNumber.parse('42') // 42 (from string)
|
||||
coercedNumber.parse(42) // 42 (already number)
|
||||
```
|
||||
|
||||
**Combining methods correctly:**
|
||||
|
||||
```typescript
|
||||
// Input: string -> Coerce to number -> Validate positive -> Transform to dollars
|
||||
const priceSchema = z.coerce
|
||||
.number()
|
||||
.refine((n) => n >= 0, 'Price cannot be negative')
|
||||
.transform((cents) => `$${(cents / 100).toFixed(2)}`)
|
||||
|
||||
priceSchema.parse('1999') // "$19.99"
|
||||
priceSchema.parse('-100') // ZodError: Price cannot be negative
|
||||
```
|
||||
|
||||
**Order of operations:**
|
||||
|
||||
```typescript
|
||||
const schema = z
|
||||
.preprocess(val => val, z.string()) // 1. preprocess (before type check)
|
||||
.transform(s => s.trim()) // 2. transform (after type check)
|
||||
.refine(s => s.length > 0) // 3. refine (custom validation)
|
||||
.transform(s => s.toUpperCase()) // 4. another transform
|
||||
|
||||
// Input flows: preprocess -> type check -> transforms/refines in order
|
||||
```
|
||||
|
||||
**Use case comparison:**
|
||||
|
||||
| Need | Method | Example |
|
||||
|------|--------|---------|
|
||||
| Convert string to number | `z.coerce.number()` | Form input |
|
||||
| Validate number is positive | `.refine(n => n > 0)` | Business rule |
|
||||
| Convert cents to dollars | `.transform(n => n/100)` | Display format |
|
||||
| Trim whitespace before check | `z.preprocess` | Input cleanup |
|
||||
|
||||
**When NOT to use this pattern:**
|
||||
- Simple type coercion: use `z.coerce.*`
|
||||
- Simple validation: use built-in methods like `.min()`, `.email()`
|
||||
|
||||
Reference: [Zod API - transform](https://zod.dev/api#transform)
|
||||
Reference in New Issue
Block a user