如何用TypeScript构建支持OR/AND逻辑、范围/多选筛选的可扩展通用筛选类
面向对象的可扩展筛选器实现方案
核心设计思路是遵循开放封闭原则:将不同筛选类型的处理逻辑从主类中抽离,通过策略模式解耦,新增筛选类型时无需修改核心生成逻辑,只需要新增独立的处理器并注册即可。
第一步:定义通用类型约束
先把涉及的结构做强类型约束,避免散落的any类型带来的运行时错误:
// 用户输入的筛选项基础结构 interface BaseFilterItem<T = unknown> { field: string; type: string; value: T; } // 后端要求的筛选节点结构(兼容嵌套、运算符、普通条件) type FilterNode = | { field: string; operator: string; value: unknown } | 'AND' | 'OR' | FilterNode[];
第二步:定义统一的筛选处理器接口
所有筛选类型的处理逻辑都要遵循这个统一接口,保证主类调用逻辑一致:
interface FilterTypeHandler<T extends BaseFilterItem = BaseFilterItem> { /** * 处理单个筛选项,输出符合后端格式的筛选节点 * @param item 用户输入的单个筛选项 */ handle(item: T): FilterNode; }
第三步:将原有硬编码逻辑拆为独立处理器
把原来createFilter方法里写死的default、range处理逻辑,抽成独立的实现类,每个类只负责一类筛选的转换:
默认等值筛选处理器
class DefaultFilterHandler implements FilterTypeHandler<BaseFilterItem<number | string>> { handle(item) { return { field: item.field, operator: '=', value: item.value }; } }
范围筛选处理器
class RangeFilterHandler implements FilterTypeHandler<BaseFilterItem<string>> { handle(item) { // 解析"20 - 30"格式的范围值,做容错处理 const [min, max] = item.value.split('-').map(str => str.trim()); return [ { field: item.field, operator: '>=', value: min }, 'AND', { field: item.field, operator: '<=', value: max } ]; } }
第四步:重构核心生成类
原来的主类去掉硬编码的类型判断逻辑,内部维护一个处理器注册表,支持外部按需注册筛选类型的处理器,通用的节点拼接逻辑保留不变:
export class FilterBuilder { // 最终生成的筛选结构 private filter: FilterNode[] = []; // 筛选类型到对应处理器的映射表 private handlerRegistry: Map<string, FilterTypeHandler> = new Map(); /** * 注册新的筛选类型处理器,支持链式调用 * @param type 筛选项的类型标识(如'default'/'range'/'multiSelect') * @param handler 对应的处理器实例 */ registerHandler(type: string, handler: FilterTypeHandler) { this.handlerRegistry.set(type, handler); return this; } /** * 根据用户输入生成符合后端格式的筛选结构 * @param receivedFields 用户传入的筛选项数组 */ createFilter(receivedFields: BaseFilterItem[]) { // 清空上次生成的结果 this.filter = []; const processedNodes: FilterNode[] = []; for (const item of receivedFields) { // 从注册表匹配对应类型的处理器 const handler = this.handlerRegistry.get(item.type); if (!handler) { console.warn(`跳过未配置处理器的筛选项:${item.field}(类型${item.type})`); continue; } // 调用处理器完成单个筛选项的转换 processedNodes.push(handler.handle(item)); } // 通用拼接逻辑:所有处理完成的节点之间用OR连接,和原有逻辑完全一致 this.filter = processedNodes .map((node, index) => index < processedNodes.length - 1 ? [node, 'OR'] : [node]) .flat(); return this.filter; } }
使用方式
// 初始化生成器,注册现有筛选类型的处理器 const filterBuilder = new FilterBuilder() .registerHandler('default', new DefaultFilterHandler()) .registerHandler('range', new RangeFilterHandler()); // 传入用户输入的筛选项 const userInput = [ { field: 'id', type: 'default', value: 12 }, { field: 'id2', type: 'default', value: 13 }, { field: 'name', type: 'default', value: 'yulya' }, { field: 'age', type: 'range', value: '20 - 30' } ]; // 直接生成符合要求的后端请求结构 const requestFilter = filterBuilder.createFilter(userInput);
扩展示例
未来需要新增多选筛选(对应后端IN操作符)时,完全不需要修改核心的FilterBuilder类,只需要新增对应的处理器并注册即可:
// 新增多选筛选的处理器 class MultiSelectFilterHandler implements FilterTypeHandler<BaseFilterItem<unknown[]>> { handle(item) { return { field: item.field, operator: 'IN', value: item.value }; } } // 注册后即可直接使用 filterBuilder.registerHandler('multiSelect', new MultiSelectFilterHandler());
这种实现方式的优势:
- 核心生成逻辑只负责流程调度,没有硬编码的类型判断,修改现有类型的处理逻辑时不会影响其他类型
- 每个筛选处理器的逻辑独立,可单独做单元测试
- 新增筛选类型不需要改动原有已运行的代码,避免引入回归问题
- 全链路TS类型约束,写代码时就能捕获类型不匹配的错误
内容的提问来源于stack exchange,提问作者kuroi.kasa
相关产品推荐
相关产品推荐

