Files
makemd/docs/01_Architecture/15_Schema_Driven_Development.md
wurenzhi 15ee1758f5 refactor: 重构项目结构并优化类型定义
- 移除extension模块,将功能迁移至node-agent
- 修复类型导出问题,使用export type明确类型导出
- 统一数据库连接方式,从直接导入改为使用config/database
- 更新文档中的项目结构描述
- 添加多个服务的实用方法,如getForecast、getBalances等
- 修复类型错误和TS1205警告
- 优化RedisService调用方式
- 添加新的实体类型定义
- 更新审计日志格式,统一字段命名
2026-03-21 15:04:06 +08:00

1030 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Schema 驱动开发指南Schema-Driven Development Guide
> **模块**: 01_Architecture - Schema 驱动开发与数据验证
> **更新日期**: 2026-03-20
> **适用范围**: 全项目dashboard、server、node-agent
---
## 1. 概述Overview
### 1.1 什么是 Schema 驱动开发
**Schema 驱动开发**是一种以数据结构定义为核心的软件开发方法,通过定义清晰的数据 Schema 来:
- **统一数据模型**:确保前后端数据结构一致
- **自动类型推导**:从 Schema 自动生成 TypeScript 类型
- **运行时验证**:在运行时验证数据完整性
- **减少错误**:通过编译时和运行时双重检查减少类型错误
### 1.2 核心优势
| 优势 | 说明 | 效果 |
|------|------|------|
| **类型安全** | 编译时 + 运行时双重检查 | 减少 80% 类型错误 |
| **自动推导** | 从 Schema 自动生成类型 | 减少重复定义 |
| **数据验证** | 自动验证输入输出 | 防止脏数据 |
| **文档化** | Schema 即文档 | 提高可维护性 |
| **AI 友好** | 清晰的数据结构定义 | AI 更容易理解 |
### 1.3 工具选择
| 工具 | 类型 | 推荐场景 | 优先级 |
|------|------|----------|--------|
| **zod** | Schema 库 | 首选推荐 | P0 |
| **class-validator** | 装饰器 | 类验证 | P1 |
| **io-ts** | 函数式 | 函数式编程 | P2 |
| **yup** | Schema 库 | 旧项目迁移 | P3 |
---
## 2. zod 快速入门zod Quick Start
### 2.1 安装
```bash
npm install zod
npm install -D @types/zod
```
### 2.2 基础用法
```typescript
import { z } from 'zod'
// 定义 Schema
const UserSchema = z.object({
id: z.string().uuid(),
name: z.string().min(1).max(100),
email: z.string().email(),
age: z.number().int().positive(),
isActive: z.boolean().default(true),
createdAt: z.date().default(() => new Date())
})
// 推导类型
type User = z.infer<typeof UserSchema>
// 验证数据
const userData = {
id: '123e4567-e89b-12d3-a456-426614174000',
name: 'John Doe',
email: 'john@example.com',
age: 30
}
const user = UserSchema.parse(userData)
console.log(user) // { id: '...', name: 'John Doe', email: 'john@example.com', age: 30, isActive: true, createdAt: Date }
// 安全解析(不抛出异常)
const result = UserSchema.safeParse(userData)
if (result.success) {
console.log(result.data)
} else {
console.error(result.error)
}
```
### 2.3 常用 Schema 类型
```typescript
import { z } from 'zod'
// 字符串
const StringSchema = z.string()
const EmailSchema = z.string().email()
const UrlSchema = z.string().url()
const UUIDSchema = z.string().uuid()
// 数字
const NumberSchema = z.number()
const PositiveNumberSchema = z.number().positive()
const IntSchema = z.number().int()
const MinMaxSchema = z.number().min(0).max(100)
// 布尔值
const BooleanSchema = z.boolean()
// 日期
const DateSchema = z.date()
// 数组
const ArraySchema = z.array(z.string())
const NonEmptyArraySchema = z.array(z.string()).nonempty()
const MinLengthArraySchema = z.array(z.string()).min(1)
// 对象
const ObjectSchema = z.object({
name: z.string(),
age: z.number()
})
// 可选字段
const OptionalSchema = z.object({
name: z.string(),
age: z.number().optional()
})
// 可空字段
const NullableSchema = z.object({
name: z.string().nullable()
})
// 联合类型
const UnionSchema = z.union([z.string(), z.number()])
const LiteralSchema = z.literal('hello')
// 枚举
const EnumSchema = z.enum(['active', 'inactive', 'pending'])
// 任意类型
const AnySchema = z.any()
const UnknownSchema = z.unknown()
// 自定义验证
const CustomSchema = z.string().refine(
(val) => val.length >= 3,
{ message: 'String must be at least 3 characters' }
)
// 转换
const TransformSchema = z.string().transform((val) => val.toUpperCase())
const CoerceSchema = z.coerce.number() // 自动转换字符串为数字
```
---
## 3. 项目 Schema 组织Project Schema Organization
### 3.1 目录结构
```
/src
/schemas
/api # API 请求/响应 Schema
/user
CreateUserRequest.schema.ts
CreateUserResponse.schema.ts
GetUserResponse.schema.ts
/product
CreateProductRequest.schema.ts
ProductResponse.schema.ts
/dto # 数据传输对象 Schema
UserDTO.schema.ts
ProductDTO.schema.ts
/domain # 领域模型 Schema
User.schema.ts
Product.schema.ts
Order.schema.ts
/shared # 共享 Schema
Pagination.schema.ts
Error.schema.ts
```
### 3.2 命名规范
| 类型 | 命名规则 | 示例 |
|------|----------|------|
| **Schema 文件** | `{Name}.schema.ts` | `User.schema.ts` |
| **Schema 变量** | `{Name}Schema` | `UserSchema` |
| **类型推导** | `{Name}` | `User` |
| **DTO Schema** | `{Name}DTOSchema` | `UserDTOSchema` |
| **API 请求** | `{Action}{Resource}RequestSchema` | `CreateUserRequestSchema` |
| **API 响应** | `{Action}{Resource}ResponseSchema` | `CreateUserResponseSchema` |
---
## 4. 领域模型 SchemaDomain Model Schemas
### 4.1 User Schema
```typescript
// src/schemas/domain/User.schema.ts
import { z } from 'zod'
export const UserSchema = z.object({
id: z.string().uuid(),
merchantId: z.string().uuid(),
username: z.string().min(3).max(50),
email: z.string().email(),
role: z.enum(['ADMIN', 'MANAGER', 'OPERATOR', 'VIEWER']),
status: z.enum(['ACTIVE', 'INACTIVE', 'SUSPENDED']).default('ACTIVE'),
createdAt: z.date(),
updatedAt: z.date()
})
export type User = z.infer<typeof UserSchema>
```
### 4.2 Product Schema
```typescript
// src/schemas/domain/Product.schema.ts
import { z } from 'zod'
export const ProductSchema = z.object({
id: z.string().uuid(),
merchantId: z.string().uuid(),
storeId: z.string().uuid(),
name: z.string().min(1).max(200),
sku: z.string().min(1).max(50),
price: z.number().positive(),
cost: z.number().nonnegative(),
stock: z.number().int().nonnegative(),
status: z.enum(['ACTIVE', 'INACTIVE', 'OUT_OF_STOCK']).default('ACTIVE'),
images: z.array(z.string().url()),
attributes: z.record(z.string(), z.unknown()).optional(),
createdAt: z.date(),
updatedAt: z.date()
})
export type Product = z.infer<typeof ProductSchema>
```
### 4.3 Order Schema
```typescript
// src/schemas/domain/Order.schema.ts
import { z } from 'zod'
export const OrderItemSchema = z.object({
productId: z.string().uuid(),
productName: z.string(),
quantity: z.number().int().positive(),
unitPrice: z.number().positive(),
totalPrice: z.number().positive()
})
export const OrderSchema = z.object({
id: z.string().uuid(),
merchantId: z.string().uuid(),
userId: z.string().uuid(),
orderNumber: z.string().min(1),
status: z.enum([
'PENDING',
'PAID',
'PROCESSING',
'SHIPPED',
'COMPLETED',
'CANCELLED',
'REFUNDED'
]).default('PENDING'),
items: z.array(OrderItemSchema).nonempty(),
subtotal: z.number().positive(),
shippingFee: z.number().nonnegative(),
tax: z.number().nonnegative(),
totalAmount: z.number().positive(),
shippingAddress: z.object({
recipientName: z.string(),
street: z.string(),
city: z.string(),
state: z.string(),
zipCode: z.string(),
country: z.string()
}),
paymentMethod: z.enum(['CREDIT_CARD', 'PAYPAL', 'BANK_TRANSFER']),
createdAt: z.date(),
updatedAt: z.date()
})
export type Order = z.infer<typeof OrderSchema>
export type OrderItem = z.infer<typeof OrderItemSchema>
```
---
## 5. DTO SchemaData Transfer Object Schemas
### 5.1 User DTO
```typescript
// src/schemas/dto/UserDTO.schema.ts
import { z } from 'zod'
export const UserDTOSchema = z.object({
id: z.string().uuid(),
username: z.string(),
email: z.string().email(),
role: z.enum(['ADMIN', 'MANAGER', 'OPERATOR', 'VIEWER']),
status: z.enum(['ACTIVE', 'INACTIVE', 'SUSPENDED'])
})
export type UserDTO = z.infer<typeof UserDTOSchema>
// 转换函数:从领域模型到 DTO
export function toUserDTO(user: unknown): UserDTO {
return UserDTOSchema.parse(user)
}
```
### 5.2 Product DTO
```typescript
// src/schemas/dto/ProductDTO.schema.ts
import { z } from 'zod'
export const ProductDTOSchema = z.object({
id: z.string().uuid(),
name: z.string(),
sku: z.string(),
price: z.number(),
stock: z.number(),
status: z.enum(['ACTIVE', 'INACTIVE', 'OUT_OF_STOCK']),
imageUrl: z.string().url().optional()
})
export type ProductDTO = z.infer<typeof ProductDTOSchema>
export function toProductDTO(product: unknown): ProductDTO {
const validated = ProductDTOSchema.parse(product)
return {
...validated,
imageUrl: validated.images?.[0]
}
}
```
---
## 6. API SchemaAPI Request/Response Schemas
### 6.1 用户 API
```typescript
// src/schemas/api/user/CreateUserRequest.schema.ts
import { z } from 'zod'
export const CreateUserRequestSchema = z.object({
username: z.string().min(3).max(50),
email: z.string().email(),
password: z.string().min(8).regex(/[A-Z]/).regex(/[0-9]/),
role: z.enum(['ADMIN', 'MANAGER', 'OPERATOR', 'VIEWER']).default('OPERATOR')
})
export type CreateUserRequest = z.infer<typeof CreateUserRequestSchema>
```
```typescript
// src/schemas/api/user/CreateUserResponse.schema.ts
import { z } from 'zod'
export const CreateUserResponseSchema = z.object({
success: z.boolean(),
data: z.object({
id: z.string().uuid(),
username: z.string(),
email: z.string(),
role: z.string(),
createdAt: z.date()
}),
message: z.string().optional()
})
export type CreateUserResponse = z.infer<typeof CreateUserResponseSchema>
```
```typescript
// src/schemas/api/user/GetUserResponse.schema.ts
import { z } from 'zod'
export const GetUserResponseSchema = z.object({
success: z.boolean(),
data: z.object({
id: z.string().uuid(),
username: z.string(),
email: z.string(),
role: z.string(),
status: z.string(),
createdAt: z.date(),
updatedAt: z.date()
})
})
export type GetUserResponse = z.infer<typeof GetUserResponseSchema>
```
### 6.2 商品 API
```typescript
// src/schemas/api/product/CreateProductRequest.schema.ts
import { z } from 'zod'
export const CreateProductRequestSchema = z.object({
storeId: z.string().uuid(),
name: z.string().min(1).max(200),
sku: z.string().min(1).max(50),
price: z.number().positive(),
cost: z.number().nonnegative(),
stock: z.number().int().nonnegative(),
images: z.array(z.string().url()),
attributes: z.record(z.string(), z.unknown()).optional()
})
export type CreateProductRequest = z.infer<typeof CreateProductRequestSchema>
```
```typescript
// src/schemas/api/product/ProductResponse.schema.ts
import { z } from 'zod'
export const ProductResponseSchema = z.object({
success: z.boolean(),
data: z.object({
id: z.string().uuid(),
name: z.string(),
sku: z.string(),
price: z.number(),
stock: z.number(),
status: z.string(),
imageUrl: z.string().url().optional(),
createdAt: z.date(),
updatedAt: z.date()
})
})
export type ProductResponse = z.infer<typeof ProductResponseSchema>
```
---
## 7. 共享 SchemaShared Schemas
### 7.1 分页 Schema
```typescript
// src/schemas/shared/Pagination.schema.ts
import { z } from 'zod'
export const PaginationParamsSchema = z.object({
page: z.number().int().positive().default(1),
pageSize: z.number().int().positive().max(100).default(20)
})
export type PaginationParams = z.infer<typeof PaginationParamsSchema>
export const PaginatedResponseSchema = <T extends z.ZodType>(itemSchema: T) =>
z.object({
success: z.boolean(),
data: z.object({
items: z.array(itemSchema),
total: z.number().int().nonnegative(),
page: z.number().int().positive(),
pageSize: z.number().int().positive(),
totalPages: z.number().int().nonnegative()
})
})
export type PaginatedResponse<T> = z.infer<ReturnType<typeof PaginatedResponseSchema<z.ZodType<T>>>>
```
### 7.2 错误响应 Schema
```typescript
// src/schemas/shared/Error.schema.ts
import { z } from 'zod'
export const ErrorResponseSchema = z.object({
success: z.literal(false),
error: z.object({
code: z.string(),
message: z.string(),
details: z.array(z.object({
field: z.string(),
message: z.string()
})).optional()
}),
timestamp: z.date()
})
export type ErrorResponse = z.infer<typeof ErrorResponseSchema>
export function createErrorResponse(
code: string,
message: string,
details?: Array<{ field: string; message: string }>
): ErrorResponse {
return ErrorResponseSchema.parse({
success: false,
error: { code, message, details },
timestamp: new Date()
})
}
```
---
## 8. Schema 使用实践Schema Usage Practices
### 8.1 在 Controller 中使用
```typescript
// src/api/controllers/UserController.ts
import { Request, Response } from 'express'
import { CreateUserRequestSchema, CreateUserResponseSchema } from '@/schemas/api/user'
import { UserService } from '@/services/UserService'
export class UserController {
constructor(private readonly userService: UserService) {}
async createUser(req: Request, res: Response): Promise<void> {
try {
// 验证请求数据
const requestData = CreateUserRequestSchema.parse(req.body)
// 调用服务
const user = await this.userService.createUser(requestData)
// 构建响应
const responseData = CreateUserResponseSchema.parse({
success: true,
data: {
id: user.id,
username: user.username,
email: user.email,
role: user.role,
createdAt: user.createdAt
}
})
res.json(responseData)
} catch (error) {
if (error instanceof z.ZodError) {
res.status(400).json({
success: false,
error: {
code: 'VALIDATION_ERROR',
message: 'Invalid request data',
details: error.errors.map(err => ({
field: err.path.join('.'),
message: err.message
}))
},
timestamp: new Date()
})
} else {
res.status(500).json({
success: false,
error: {
code: 'INTERNAL_ERROR',
message: error instanceof Error ? error.message : 'Unknown error'
},
timestamp: new Date()
})
}
}
}
}
```
### 8.2 在 Service 中使用
```typescript
// src/services/UserService.ts
import { UserSchema, type User } from '@/schemas/domain/User.schema'
import { UserRepository } from '@/repositories/UserRepository'
export class UserService {
constructor(private readonly userRepository: UserRepository) {}
async createUser(data: {
username: string
email: string
password: string
role: string
}): Promise<User> {
// 创建用户实体
const user = UserSchema.parse({
id: crypto.randomUUID(),
merchantId: data.merchantId,
username: data.username,
email: data.email,
role: data.role as any,
status: 'ACTIVE',
createdAt: new Date(),
updatedAt: new Date()
})
// 保存到数据库
return await this.userRepository.create(user)
}
async getUserById(id: string): Promise<User | null> {
const userData = await this.userRepository.findById(id)
if (!userData) {
return null
}
// 验证数据符合 Schema
return UserSchema.parse(userData)
}
}
```
### 8.3 在 Repository 中使用
```typescript
// src/repositories/UserRepository.ts
import { Knex } from 'knex'
import { UserSchema, type User } from '@/schemas/domain/User.schema'
export class UserRepository {
constructor(private readonly db: Knex) {}
async create(user: User): Promise<User> {
const [createdUser] = await this.db('users')
.insert({
id: user.id,
merchant_id: user.merchantId,
username: user.username,
email: user.email,
role: user.role,
status: user.status,
created_at: user.createdAt,
updated_at: user.updatedAt
})
.returning('*')
// 验证并返回
return UserSchema.parse(this.mapToEntity(createdUser))
}
async findById(id: string): Promise<User | null> {
const row = await this.db('users').where({ id }).first()
if (!row) {
return null
}
return UserSchema.parse(this.mapToEntity(row))
}
private mapToEntity(row: any): User {
return {
id: row.id,
merchantId: row.merchant_id,
username: row.username,
email: row.email,
role: row.role,
status: row.status,
createdAt: new Date(row.created_at),
updatedAt: new Date(row.updated_at)
}
}
}
```
---
## 9. 前端 Schema 使用Frontend Schema Usage
### 9.1 API 调用验证
```typescript
// src/services/api/user.ts
import axios from 'axios'
import { z } from 'zod'
import { GetUserResponseSchema, type GetUserResponse } from '@/schemas/api/user/GetUserResponse.schema'
export async function getUser(userId: string): Promise<GetUserResponse> {
const response = await axios.get(`/api/users/${userId}`)
// 验证响应数据
return GetUserResponseSchema.parse(response.data)
}
```
### 9.2 表单验证
```typescript
// src/components/UserForm.tsx
import { useState } from 'react'
import { z } from 'zod'
import { CreateUserRequestSchema } from '@/schemas/api/user/CreateUserRequest.schema'
export function UserForm() {
const [formData, setFormData] = useState({
username: '',
email: '',
password: '',
role: 'OPERATOR'
})
const [errors, setErrors] = useState<Record<string, string>>({})
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault()
try {
// 验证表单数据
CreateUserRequestSchema.parse(formData)
// 提交数据
submitForm(formData)
} catch (error) {
if (error instanceof z.ZodError) {
const errorMap: Record<string, string> = {}
error.errors.forEach(err => {
const field = err.path.join('.')
errorMap[field] = err.message
})
setErrors(errorMap)
}
}
}
return (
<form onSubmit={handleSubmit}>
<input
type="text"
value={formData.username}
onChange={(e) => setFormData({ ...formData, username: e.target.value })}
placeholder="Username"
/>
{errors.username && <span className="error">{errors.username}</span>}
<input
type="email"
value={formData.email}
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
placeholder="Email"
/>
{errors.email && <span className="error">{errors.email}</span>}
<button type="submit">Submit</button>
</form>
)
}
```
---
## 10. 高级用法Advanced Usage
### 10.1 Schema 扩展
```typescript
import { z } from 'zod'
// 基础 Schema
const BaseUserSchema = z.object({
id: z.string().uuid(),
username: z.string(),
email: z.string().email()
})
// 扩展 Schema
const ExtendedUserSchema = BaseUserSchema.extend({
role: z.enum(['ADMIN', 'MANAGER', 'OPERATOR']),
status: z.enum(['ACTIVE', 'INACTIVE'])
})
// 合并 Schema
const UserWithProfileSchema = z.object({
user: BaseUserSchema,
profile: z.object({
firstName: z.string(),
lastName: z.string(),
phone: z.string()
})
})
```
### 10.2 条件验证
```typescript
import { z } from 'zod'
const OrderSchema = z.object({
type: z.enum(['ONLINE', 'OFFLINE']),
// ONLINE 订单必须有 email
email: z.string().email().optional(),
// OFFLINE 订单必须有 phone
phone: z.string().optional()
}).refine(
(data) => {
if (data.type === 'ONLINE') {
return !!data.email
}
return !!data.phone
},
{
message: 'Email is required for ONLINE orders, Phone is required for OFFLINE orders',
path: []
}
)
```
### 10.3 自定义错误消息
```typescript
import { z } from 'zod'
const UserSchema = z.object({
username: z.string().min(3, { message: 'Username must be at least 3 characters' }),
email: z.string().email({ message: 'Invalid email format' }),
age: z.number().min(18, { message: 'Must be at least 18 years old' })
})
```
### 10.4 异步验证
```typescript
import { z } from 'zod'
const UniqueEmailSchema = z.string().email().refine(
async (email) => {
const existingUser = await userRepository.findByEmail(email)
return !existingUser
},
{ message: 'Email already exists' }
)
```
---
## 11. 性能优化Performance Optimization
### 11.1 避免重复解析
```typescript
// ❌ 错误:每次都解析
function processUserData(data: unknown) {
const user = UserSchema.parse(data)
return user.name
}
// ✅ 正确:缓存解析结果
const userCache = new Map<string, User>()
function processUserData(data: unknown) {
const cacheKey = JSON.stringify(data)
if (userCache.has(cacheKey)) {
return userCache.get(cacheKey)
}
const user = UserSchema.parse(data)
userCache.set(cacheKey, user)
return user
}
```
### 11.2 使用 safeParse 避免异常
```typescript
// ❌ 错误:使用 parse 会抛出异常
function validateData(data: unknown) {
try {
return UserSchema.parse(data)
} catch (error) {
return null
}
}
// ✅ 正确:使用 safeParse
function validateData(data: unknown) {
const result = UserSchema.safeParse(data)
return result.success ? result.data : null
}
```
---
## 12. 测试 SchemaTesting Schemas
### 12.1 单元测试
```typescript
// src/schemas/__tests__/User.schema.test.ts
import { describe, it, expect } from 'vitest'
import { UserSchema } from '../User.schema'
describe('UserSchema', () => {
it('should validate valid user data', () => {
const validUser = {
id: '123e4567-e89b-12d3-a456-426614174000',
merchantId: '123e4567-e89b-12d3-a456-426614174001',
username: 'john_doe',
email: 'john@example.com',
role: 'ADMIN',
status: 'ACTIVE',
createdAt: new Date(),
updatedAt: new Date()
}
const result = UserSchema.safeParse(validUser)
expect(result.success).toBe(true)
})
it('should reject invalid email', () => {
const invalidUser = {
id: '123e4567-e89b-12d3-a456-426614174000',
merchantId: '123e4567-e89b-12d3-a456-426614174001',
username: 'john_doe',
email: 'invalid-email',
role: 'ADMIN',
status: 'ACTIVE',
createdAt: new Date(),
updatedAt: new Date()
}
const result = UserSchema.safeParse(invalidUser)
expect(result.success).toBe(false)
})
it('should reject invalid UUID', () => {
const invalidUser = {
id: 'not-a-uuid',
merchantId: '123e4567-e89b-12d3-a456-426614174001',
username: 'john_doe',
email: 'john@example.com',
role: 'ADMIN',
status: 'ACTIVE',
createdAt: new Date(),
updatedAt: new Date()
}
const result = UserSchema.safeParse(invalidUser)
expect(result.success).toBe(false)
})
})
```
---
## 13. 最佳实践Best Practices
### 13.1 Schema 设计原则
| 原则 | 说明 | 示例 |
|------|------|------|
| **单一职责** | 每个 Schema 只负责一个实体 | `UserSchema`, `ProductSchema` |
| **可复用性** | 提取公共 Schema 为共享模块 | `PaginationSchema`, `ErrorSchema` |
| **明确性** | 使用具体的类型而非 any | `z.string().email()` 而非 `z.any()` |
| **验证性** | 所有外部数据必须验证 | API 请求、数据库查询 |
| **文档化** | Schema 即文档,保持清晰 | 使用有意义的字段名 |
### 13.2 常见错误
```typescript
// ❌ 错误 1使用 any
const BadSchema = z.object({
data: z.any()
})
// ✅ 正确 1使用具体类型
const GoodSchema = z.object({
data: z.object({
id: z.string(),
name: z.string()
})
})
// ❌ 错误 2不验证外部数据
async function badHandler(req: Request) {
const user = req.body as User
return user
}
// ✅ 正确 2验证外部数据
async function goodHandler(req: Request) {
const user = UserSchema.parse(req.body)
return user
}
// ❌ 错误 3重复定义类型
interface User {
id: string
name: string
}
const UserSchema = z.object({
id: z.string(),
name: z.string()
})
// ✅ 正确 3从 Schema 推导类型
const UserSchema = z.object({
id: z.string(),
name: z.string()
})
type User = z.infer<typeof UserSchema>
```
---
## 14. 版本管理
| 版本 | 变更内容 | 日期 |
|------|----------|------|
| 1.0.0 | 初始版本 | 2026-03-20 |
---
## 15. 参考资源
- [zod 官方文档](https://zod.dev/)
- [class-validator 文档](https://github.com/typestack/class-validator)
- [Schema 驱动开发最佳实践](https://www.patterns.dev/posts/schema-driven-development/)