02-Drizzle-Zod类型系统设计
分类:elysia
发布于:
阅读时间:36 分钟
1.1 基础 Schema 结构
所有文件都遵循相同的基础 Schema 模式:
// =<mark> 基础 Schema </mark>=
const Insert = createInsertSchema(table);
const UpdateBase/Update = createUpdateSchema(table); // 有差异
const Select = createSelectSchema(table);
关键差异点:
- category, product, ads, site-config, media: 使用 UpdateBase
- auth: 直接使用 Update
1.2 业务 Schema 结构模式
标准模式(最完整):
const Create = Insert.omit({
id: true,
createdAt: true,
updatedAt: true,
});
const Update = UpdateBase.omit({
id: true,
createdAt: true,
updatedAt: true,
});
const Patch = UpdateBase.omit({
id: true,
createdAt: true,
updatedAt: true,
}).partial();
查询 Schema 模式:
const BusinessQuery = z.object({
// 业务特定的查询字段
});
const ListQuery = BusinessQuery.extend(PaginationParams.shape).extend(SortParams.shape);
1.3 特殊 Schema 命名规则
各文件中的特殊 Schema:
| 文件 | 特殊 Schema | 用途
|
|-------------|----------------------------------------------------------------------|-------------------|
| category | FormUpsert, TreeQuery, Entityone |
表单upsert、树形查询、单实体 |
| product | ProductTemplateModel, ProductImagesModel | 子模型扩展
|
| auth | Login, ChangePassword | 认证相关操作
|
| media | UploadFileDto, UploadFilesDto, BatchUpload, BatchDelete, BatchUpdate | 文件上传和批量操作
|
| ads | BatchStatusUpdate | 批量状态更新
|
| site-config | CategoryQuery, KeysQuery, BatchUpdate | 配置特定查询和批量更新
|
2. Update 和 UpdateBase 的区别分析
2.1 使用 UpdateBase 的文件(推荐模式):
- category, product, ads, site-config, media
const UpdateBase = createUpdateSchema(table);
const Update = UpdateBase.omit({
id: true,
createdAt: true,
updatedAt: true,
});
2.2 直接使用 Update 的文件:
- auth
const Update = createUpdateSchema(usersTable);
// 没有 UpdateBase,直接在 Patch 中使用
分析结论: UpdateBase 模式更规范,提供了更好的中间层抽象。
3. Create 和 Insert 的区别
所有文件都遵循相同的模式:
const Create = Insert.omit({
id: true,
createdAt: true,
updatedAt: true,
});
部分文件有额外扩展:
- product: 扩展了关联字段(templateId, categoryIds, imageIds)
- media: 扩展了可选字段(url, key, fileSize等)
4. Patch 的定义方式
4.1 标准模式(推荐):
const Patch = UpdateBase.omit({
id: true,
createdAt: true,
updatedAt: true,
}).partial();
4.2 特殊处理:
- auth: 额外排除了 password 字段
- product: 使用了复杂的链式操作
5. 值聚合和类型聚合的导出结构
5.1 标准导出模式:
// =<mark> 1. 运行时 Schema 集合(值)</mark>=
export const ModelName = {
Insert,
Update,
Select,
Create,
Patch,
ListQuery,
Entity,
// ... 其他特殊 Schema
} as const;
// =<mark> 2. 编译时类型集合(类型)</mark>=
export type ModelName = {
Insert: z.infer<typeof Insert>;
Update: z.infer<typeof Update>;
Select: z.infer<typeof Select>;
Create: z.infer<typeof Create>;
Patch: z.infer<typeof Patch>;
ListQuery: z.infer<typeof ListQuery>;
Entity: z.infer<typeof Entity>;
// ... 其他类型
};
6. 最规范和完整的文件推荐
综合评分最高的文件:
🥇 第一名:media.model.ts
优点:
- 结构最完整,包含所有标准 Schema
- 枚举定义清晰(FileType, StorageProvider)
- 批量操作 Schema 齐全(BatchUpload, BatchDelete, BatchUpdate)
- 上传相关 Schema 专门定义(UploadFileDto, UploadFilesDto)
- Entity 扩展合理(确保返回完整 URL)
- 注释详细,代码可读性高
🥈 第二名:site-config.model.ts
优点:
- 严格遵循 UpdateBase 模式
- 业务验证规则完善
- 特殊查询 Schema 定义合理(CategoryQuery, KeysQuery)
- 结构清晰简洁
🥉 第三名:category.model.ts
优点:
- 基础结构完整
- 特殊 Schema 有明确的业务用途
- 使用了 UpdateBase 模式
7. 标准化建议
7.1 推荐的标准模板:
/**
* [模块名称]模型定义
* [模块描述]
*/
import {
createInsertSchema,
createSelectSchema,
createUpdateSchema,
} from "drizzle-zod";
import { z } from "zod";
import { PaginationParams, SortParams } from "../helper/query-types";
import { [tableName] } from "./[module].schema";
// =<mark> 枚举定义(如果需要)</mark>=
export const [EnumName] = z.enum([...]);
export type [EnumName] = z.infer<typeof [EnumName]>;
// =<mark> 基础 Schema </mark>=
const Insert = createInsertSchema([tableName], {
// 字段验证规则(可选)
});
const UpdateBase = createUpdateSchema([tableName], {
// 字段验证规则(可选)
});
const Select = createSelectSchema([tableName]);
// =<mark> 业务 Schema </mark>=
const Create = Insert.omit({
id: true,
createdAt: true,
updatedAt: true,
}).extend({
// 扩展字段(可选)
});
const Update = UpdateBase.omit({
id: true,
createdAt: true,
updatedAt: true,
}).extend({
// 扩展字段(可选)
});
const Patch = UpdateBase.omit({
id: true,
createdAt: true,
updatedAt: true,
}).partial();
const BusinessQuery = z.object({
// 业务查询字段
});
const ListQuery = BusinessQuery.extend(PaginationParams.shape).extend(
SortParams.shape
);
const Entity = Select.extend({
// 实体扩展字段(可选)
});
// =<mark> 特殊业务 Schema(根据需要)</mark>=
const [SpecialSchema] = z.object({
// 特殊业务逻辑
});
// =<mark> 值聚合导出 </mark>=
export const [ModelName] = {
// 基础 Schema
Insert,
Update,
Select,
// 业务 Schema
Create,
Patch,
ListQuery,
Entity,
BusinessQuery,
// 枚举(如果有)
[EnumName],
// 特殊 Schema(如果有)
[SpecialSchema],
} as const;
// =<mark> 类型聚合导出 </mark>=
export type [ModelName] = {
Insert: z.infer<typeof Insert>;
Update: z.infer<typeof Update>;
Select: z.infer<typeof Select>;
Create: z.infer<typeof Create>;
Patch: z.infer<typeof Patch>;
ListQuery: z.infer<typeof ListQuery>;
Entity: z.infer<typeof Entity>;
BusinessQuery: z.infer<typeof BusinessQuery>;
// 枚举类型(如果有)
[EnumName]: z.infer<typeof [EnumName]>;
// 特殊类型(如果有)
[SpecialSchema]: z.infer<typeof [SpecialSchema]>;
};
7.2 关键规范建议:
8. 始终使用 UpdateBase 模式
9. 保持 Create/Update/Patch 的一致性
10. 为复杂业务场景定义专门的 Schema
11. Entity 应该包含前端展示需要的所有字段
12. 批量操作使用标准的命名模式(BatchXxx)
13. 添加必要的注释和类型文档
这个分析为项目的类型系统标准化提供了明确的指导方向,建议以 media.model.ts 作为主要参考模板