# 第一阶段 · 模块一 · 第十五节:Hook Schemas
什么是 Hook Schemas?Claude Code 如何定义 Hook 类型?Zod Schema 在 Hook 验证中的作用是什么?如何解决 Hook 循环依赖?
Claude Code 全局架构
┌─────────────────────────────────────────────────────────────────────┐
│ 核心层(core/) │
│ │
│ Hook Schemas ← 本节 │
│ ├── Zod Schema 定义 │
│ ├── 循环依赖解决 │
│ └── 类型安全 │
└─────────────────────────────────────────────────────────────────────┘
源码位置:`src/core/hooks/schemas.ts`
Hook Schema 使用 Zod 定义 Hook 的输入输出类型,用于类型验证和自动补全。
┌─────────────────────────────────────────────────────────────────────┐
│ Hook Schema 工作流程 │
│ │
│ Hook 定义 ──► Schema 验证 ──► 类型推断 │
│ │ │
│ Zod 解析 │
└─────────────────────────────────────────────────────────────────────┘
// Zod 示例
import { z } from 'zod'
// 定义 Schema
const UserSchema = z.object({
name: z.string(),
age: z.number(),
email: z.string().email()
})
// 验证
const result = UserSchema.safeParse({
name: 'John',
age: 30,
email: 'john@example.com'
})
问 1:为什么使用 Zod 而不是 TypeScript 类型?
// TypeScript 类型:
// - 编译时检查
// - 运行时无效
// Zod Schema:
// - 运行时验证
// - 可用于 API 验证
// - 自动类型推断
源码位置:`src/core/hooks/schemas.ts`
// Hook 输入输出 Schema
const HookInputSchema = z.object({
type: z.enum(['before', 'after', 'error']),
context: z.object({
sessionId: z.string(),
userId: z.string().optional()
}),
payload: z.record(z.unknown())
})
const HookOutputSchema = z.object({
success: z.boolean(),
modifiedPayload: z.record(z.unknown()).optional(),
error: z.string().optional()
})
// 特定 Hook 的 Schema
export const preToolCallSchema = z.object({
toolName: z.string(),
args: z.record(z.unknown()),
context: z.object({
sessionId: z.string()
})
})
export const postToolCallSchema = z.object({
toolName: z.string(),
args: z.record(z.unknown()),
result: z.unknown(),
context: z.object({
sessionId: z.string()
})
})
// Schema 组合
const combinedSchema = z.object({
// 使用已有 Schema
hookInput: HookInputSchema,
// 内联定义
metadata: z.object({
timestamp: z.number(),
source: z.string()
})
})
┌─────────────────────────────────────────────────────────────────────┐
│ 循环依赖示例 │
│ │
│ Hook Schema ──► 需要引用 Hook 类型 │
│ │ │
│ ▼ │
│ Hook 类型 ──► 需要引用 Hook Schema │
└─────────────────────────────────────────────────────────────────────┘
源码位置:`src/core/hooks/lazySchema.ts`
// 使用 Zod lazy 解决循环依赖
import { z } from 'zod'
// 使用 lazy 创建延迟引用
const HookSchema: z.ZodType<Hook> = z.lazy(() =>
z.object({
type: z.string(),
name: z.string(),
handler: z.function(), // 延迟定义
schema: HookSchema // 引用自己
})
)
// 类型导出
type Hook = z.infer<typeof HookSchema>
type HookInput = z.infer<typeof HookInputSchema>
type HookOutput = z.infer<typeof HookOutputSchema>
// 使用类型
function registerHook(hook: Hook): void {
// hook 已经被类型验证
}
// Hook 验证
function validateHook(hook: unknown): Hook {
const result = HookSchema.safeParse(hook)
if (!result.success) {
throw new Error(
`Invalid hook: ${result.error.message}`
)
}
return result.data
}
// 自定义验证规则
const customSchema = z.object({
name: z.string()
.min(3, 'Name must be at least 3 characters')
.max(50, 'Name must be at most 50 characters'),
handler: z.function()
.args(z.string())
.returns(z.string())
})
问 1:Schema 验证会影响性能吗?
// 性能影响:
// 1. 启动时
// - Schema 解析一次
// - 影响很小
// 2. 运行时
// - Hook 调用时验证
// - 可使用 safeParse 控制
// 3. 优化方法
// - 缓存验证结果
// - 生产环境可跳过
答案:
// 最佳实践:
// 1. 使用 Zod 原语
// - string(), number(), boolean()
// - 避免过度复杂
// 2. 明确错误消息
// - 提供有意义的错误提示
// 3. 合理拆分
// - 按功能拆分 Schema
// - 便于复用
// 4. 文档同步
// - Schema 即文档
// - 保持同步
答案:
// 方法:
// 1. 拆分
// - 按层级拆分 Schema
// - 逐层引用
// 2. 使用递归
// - Zod 支持递归定义
// - 如 lazy()
// 3. 使用 transform
// - 简化数据结构
// - 预处理输入
答案:
// 循环依赖问题:
// 问题:HookSchema 引用 Hook 类型,Hook 类型引用 HookSchema
// 解决:使用 lazy()
const HookSchema: z.ZodType<Hook> = z.lazy(() =>
z.object({
type: z.string(),
name: z.string(),
handler: z.function(),
schema: HookSchema // 延迟引用自己
})
)
// 原理:
// - lazy() 延迟求值
// - 使用时才解析
// - 避免编译时循环
答案:
// 嵌套验证策略:
// 1. 分层验证
// - 逐层验证
// - 从内到外
// 2. 自定义验证
// - 使用 z.refine()
// - 添加业务规则
// 3. 错误收集
// - 使用 z.safeParse()
// - 收集所有错误
// 4. 性能优化
// - 缓存验证结果
// - 跳过未变化部分
| 资源 | 说明 |
| `src/core/hooks/schemas.ts` | Hook Schema 定义 |
| Zod 官方文档 | Schema 详细用法 |