如何在TypeScript中为ORM关联查询条件实现强类型定义?
实现关联查询的WHERE条件类型定义方案
一、可行方案:实现属性名拼接的类型定义
可以通过TypeScript的递归类型和模板字符串,实现支持点分隔的关联属性路径类型,同时兼容操作符逻辑。
核心类型定义
先实现递归生成属性路径的类型,将嵌套属性转换为点分隔的字符串格式:
// 提取对象的属性键,排除符号类型 type ObjectKeys<T> = T extends object ? Exclude<keyof T, symbol> : never; // 递归生成属性路径:支持嵌套对象的点分隔写法 type Path<T, D extends number = 10> = [D] extends [never] ? never : T extends object ? { [K in ObjectKeys<T>]: K extends string ? `${K}` | `${K}.${Path<T[K], Prev[D]>}` : never; }[ObjectKeys<T>] : never; // 递归深度递减的辅助类型,控制最大嵌套层级 type Prev = [never, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
接着定义支持操作符的条件值类型:
// 支持等值、模糊匹配、范围比较等操作的条件类型 type ConditionValue<T> = T | { eq?: T; like?: string; lt?: T extends number ? T : never; gt?: T extends number ? T : never; // 可根据业务需求扩展更多操作符 };
最后组合成完整的WHERE条件类型:
type WhereCondition<T> = { [K in Path<T>]?: ConditionValue<T extends { [key in K]: infer V } ? V : never>; } & { // 支持AND/OR逻辑组合 AND?: WhereCondition<T>[]; OR?: WhereCondition<T>[]; };
应用到你的模型
interface Model1{ id:string; name:string; childId:string; } interface Model1Relations{ child:Model2; } type Model1WithRelations = Model1 & Model1Relations; interface Model2{ id:string; name:string; } // 生成Model1的查询条件类型 type Model1Where = WhereCondition<Model1WithRelations>; // 合法使用示例 const validQuery: Model1Where = { "name": "Bob", "child.name": { like: "%Alice%" }, AND: [ { "id": "123" }, { "child.id": { gt: "456" } } ] };
该方案完全支持"child.name"这类点分隔的属性路径写法,同时保留操作符和逻辑组合的类型检查,解决了嵌套写法可读性差的问题。
二、替代方案:兼容两种写法的类型合并
如果递归路径类型的复杂度超出预期,可以同时支持点分隔和嵌套两种写法,通过类型合并实现灵活选择:
// 嵌套式条件类型 type NestedWhere<T> = { [K in ObjectKeys<T>]?: T[K] extends object ? ConditionValue<T[K]> | NestedWhere<T[K]> : ConditionValue<T[K]>; } & { AND?: NestedWhere<T>[]; OR?: NestedWhere<T>[] }; // 合并点分隔和嵌套两种写法 type HybridWhere<T> = WhereCondition<T> | NestedWhere<T>;
这样用户既可以用{"child.name": "Bob"},也可以用{child: {name: "Bob"}},兼顾类型安全和使用灵活性。
三、注意事项
- 递归深度:
Path类型的D参数控制最大嵌套层级,默认10层,可根据实际关联深度调整。 - 特殊类型处理:如果模型包含数组、函数等非对象类型,需要在
ObjectKeys中进一步排除,避免生成无效路径。 - 性能优化:极端复杂的模型可能导致TypeScript类型检查变慢,可通过限制递归深度或拆分类型缓解。
内容的提问来源于stack exchange,提问作者Salketer
相关产品推荐
相关产品推荐

