Added AI skills
This commit is contained in:
143
.agents/skills/zod/references/compose-lazy-recursive.md
Normal file
143
.agents/skills/zod/references/compose-lazy-recursive.md
Normal file
@@ -0,0 +1,143 @@
|
||||
---
|
||||
title: Use z.lazy() for Recursive Schemas
|
||||
impact: MEDIUM
|
||||
impactDescription: Recursive types reference themselves before definition; z.lazy() defers evaluation to enable self-referential schemas
|
||||
tags: compose, lazy, recursive, trees
|
||||
---
|
||||
|
||||
## Use z.lazy() for Recursive Schemas
|
||||
|
||||
TypeScript can't infer recursive Zod schema types automatically. Use `z.lazy()` to defer schema evaluation and manually provide the type annotation. This enables tree structures, nested comments, and other self-referential data.
|
||||
|
||||
**Incorrect (direct self-reference):**
|
||||
|
||||
```typescript
|
||||
import { z } from 'zod'
|
||||
|
||||
// This fails - categorySchema used before it's defined
|
||||
const categorySchema = z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
children: z.array(categorySchema), // Error: Block-scoped variable used before declaration
|
||||
})
|
||||
```
|
||||
|
||||
**Correct (using z.lazy with type annotation):**
|
||||
|
||||
```typescript
|
||||
import { z } from 'zod'
|
||||
|
||||
// Define the type manually
|
||||
interface Category {
|
||||
id: string
|
||||
name: string
|
||||
children: Category[]
|
||||
}
|
||||
|
||||
// Use z.lazy() to defer schema reference
|
||||
const categorySchema: z.ZodType<Category> = z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
children: z.lazy(() => z.array(categorySchema)),
|
||||
})
|
||||
|
||||
// Now it works
|
||||
const tree = categorySchema.parse({
|
||||
id: '1',
|
||||
name: 'Electronics',
|
||||
children: [
|
||||
{
|
||||
id: '2',
|
||||
name: 'Phones',
|
||||
children: [
|
||||
{ id: '3', name: 'iPhones', children: [] },
|
||||
{ id: '4', name: 'Android', children: [] },
|
||||
],
|
||||
},
|
||||
],
|
||||
})
|
||||
```
|
||||
|
||||
**Common recursive patterns:**
|
||||
|
||||
```typescript
|
||||
// Comments with replies
|
||||
interface Comment {
|
||||
id: string
|
||||
content: string
|
||||
author: string
|
||||
replies: Comment[]
|
||||
}
|
||||
|
||||
const commentSchema: z.ZodType<Comment> = z.object({
|
||||
id: z.string(),
|
||||
content: z.string(),
|
||||
author: z.string(),
|
||||
replies: z.lazy(() => z.array(commentSchema)),
|
||||
})
|
||||
|
||||
// Binary tree
|
||||
interface TreeNode {
|
||||
value: number
|
||||
left: TreeNode | null
|
||||
right: TreeNode | null
|
||||
}
|
||||
|
||||
const treeNodeSchema: z.ZodType<TreeNode> = z.object({
|
||||
value: z.number(),
|
||||
left: z.lazy(() => treeNodeSchema.nullable()),
|
||||
right: z.lazy(() => treeNodeSchema.nullable()),
|
||||
})
|
||||
|
||||
// Nested menu structure
|
||||
interface MenuItem {
|
||||
label: string
|
||||
href?: string
|
||||
children?: MenuItem[]
|
||||
}
|
||||
|
||||
const menuItemSchema: z.ZodType<MenuItem> = z.object({
|
||||
label: z.string(),
|
||||
href: z.string().url().optional(),
|
||||
children: z.lazy(() => z.array(menuItemSchema)).optional(),
|
||||
})
|
||||
```
|
||||
|
||||
**JSON Schema (any valid JSON):**
|
||||
|
||||
```typescript
|
||||
type JSONValue =
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| null
|
||||
| JSONValue[]
|
||||
| { [key: string]: JSONValue }
|
||||
|
||||
const jsonValueSchema: z.ZodType<JSONValue> = z.lazy(() =>
|
||||
z.union([
|
||||
z.string(),
|
||||
z.number(),
|
||||
z.boolean(),
|
||||
z.null(),
|
||||
z.array(jsonValueSchema),
|
||||
z.record(jsonValueSchema),
|
||||
])
|
||||
)
|
||||
```
|
||||
|
||||
**Performance consideration:**
|
||||
|
||||
```typescript
|
||||
// z.lazy() has minimal overhead - the function is called once
|
||||
// and the schema is cached. Safe to use in hot paths.
|
||||
|
||||
// If validating many recursive structures, the schema itself
|
||||
// is only built once. Validation performance depends on data depth.
|
||||
```
|
||||
|
||||
**When NOT to use this pattern:**
|
||||
- Non-recursive schemas (lazy adds unnecessary indirection)
|
||||
- When you can flatten the structure instead
|
||||
|
||||
Reference: [Zod API - Recursive Types](https://zod.dev/api#recursive-types)
|
||||
Reference in New Issue
Block a user