Added AI skills
This commit is contained in:
113
.agents/skills/zod/references/compose-pipe.md
Normal file
113
.agents/skills/zod/references/compose-pipe.md
Normal file
@@ -0,0 +1,113 @@
|
||||
---
|
||||
title: Use pipe() for Multi-Stage Validation
|
||||
impact: MEDIUM
|
||||
impactDescription: Chaining transforms loses intermediate type info; pipe() explicitly shows data flow through validation stages
|
||||
tags: compose, pipe, pipeline, transform
|
||||
---
|
||||
|
||||
## Use pipe() for Multi-Stage Validation
|
||||
|
||||
When data needs to pass through multiple validation stages (coerce string to number, then validate range, then transform to currency), use `.pipe()` to chain schemas. This makes the data transformation pipeline explicit and each stage's type clear.
|
||||
|
||||
**Incorrect (unclear transformation chain):**
|
||||
|
||||
```typescript
|
||||
import { z } from 'zod'
|
||||
|
||||
// All transforms in one long chain - hard to understand stages
|
||||
const priceSchema = z
|
||||
.string()
|
||||
.transform((s) => parseFloat(s.replace(/[$,]/g, '')))
|
||||
.refine((n) => !isNaN(n), 'Invalid number')
|
||||
.refine((n) => n >= 0, 'Must be positive')
|
||||
.refine((n) => n <= 1000000, 'Too large')
|
||||
.transform((n) => Math.round(n * 100))
|
||||
|
||||
// What type is n at each stage? Hard to tell
|
||||
```
|
||||
|
||||
**Correct (using pipe for clear stages):**
|
||||
|
||||
```typescript
|
||||
import { z } from 'zod'
|
||||
|
||||
// Stage 1: Coerce string to number
|
||||
const parsePrice = z.string().transform((s) => {
|
||||
const cleaned = s.replace(/[$,]/g, '')
|
||||
const parsed = parseFloat(cleaned)
|
||||
if (isNaN(parsed)) throw new Error('Invalid number')
|
||||
return parsed
|
||||
})
|
||||
|
||||
// Stage 2: Validate number constraints
|
||||
const validPrice = z.number().min(0, 'Must be positive').max(1000000, 'Too large')
|
||||
|
||||
// Stage 3: Transform to cents
|
||||
const centsPrice = z.number().transform((n) => Math.round(n * 100))
|
||||
|
||||
// Pipe them together - clear data flow
|
||||
const priceSchema = parsePrice.pipe(validPrice).pipe(centsPrice)
|
||||
|
||||
// Type at each stage is clear:
|
||||
// string -> number (parsePrice)
|
||||
// number -> number (validPrice)
|
||||
// number -> number (centsPrice, but semantically cents)
|
||||
```
|
||||
|
||||
**Coercion with validation:**
|
||||
|
||||
```typescript
|
||||
// Without pipe - validation runs on raw input
|
||||
const schema1 = z.coerce.number().min(1)
|
||||
schema1.parse('') // Passes! Empty string coerces to 0, but then... wait, 0 < 1
|
||||
|
||||
// With pipe - validation runs on coerced value
|
||||
const schema2 = z.coerce.number().pipe(z.number().min(1))
|
||||
schema2.parse('') // Fails correctly: 0 is less than 1
|
||||
```
|
||||
|
||||
**Complex data transformation:**
|
||||
|
||||
```typescript
|
||||
// Input: CSV string of emails
|
||||
// Output: Array of normalized, validated email objects
|
||||
|
||||
const emailArraySchema = z
|
||||
.string()
|
||||
// Stage 1: Split CSV
|
||||
.transform((s) => s.split(',').map((e) => e.trim()))
|
||||
// Stage 2: Validate as email array
|
||||
.pipe(z.array(z.string().email()))
|
||||
// Stage 3: Transform to objects
|
||||
.pipe(
|
||||
z.array(z.string()).transform((emails) =>
|
||||
emails.map((email) => ({
|
||||
address: email.toLowerCase(),
|
||||
domain: email.split('@')[1],
|
||||
}))
|
||||
)
|
||||
)
|
||||
|
||||
emailArraySchema.parse('John@Example.com, jane@test.com')
|
||||
// [
|
||||
// { address: 'john@example.com', domain: 'Example.com' },
|
||||
// { address: 'jane@test.com', domain: 'test.com' }
|
||||
// ]
|
||||
```
|
||||
|
||||
**Type inference with pipe:**
|
||||
|
||||
```typescript
|
||||
const schema = z.string().pipe(z.coerce.number()).pipe(z.number().positive())
|
||||
|
||||
type Input = z.input<typeof schema> // string
|
||||
type Output = z.output<typeof schema> // number
|
||||
|
||||
// Each pipe stage has clear input/output types
|
||||
```
|
||||
|
||||
**When NOT to use this pattern:**
|
||||
- Simple single-stage validation (adds unnecessary complexity)
|
||||
- When `.refine()` chain is sufficient and readable
|
||||
|
||||
Reference: [Zod API - pipe](https://zod.dev/api#pipe)
|
||||
Reference in New Issue
Block a user