Javascript 如何使用TypeScript形成灵活的可扩展过滤器?

Javascript 如何使用TypeScript形成灵活的可扩展过滤器?,javascript,typescript,oop,filter,filtering,Javascript,Typescript,Oop,Filter,Filtering,我需要使用运算符“OR”和“and”构建一个带有TypeScript的通用过滤器。应该有一种使用范围和多选过滤器的方法。 但我是OOP新手,不知道应该如何使用类来实现。 对后端的筛选器请求应如下所示,如果存在新类型,则必须有一种方法来扩展它们: [ { field: 'id', operator: '=', value: 12 }, 'OR', { field: 'name', operator: '=', value: 'testName' }, 'OR', [ {

我需要使用运算符“OR”和“and”构建一个带有TypeScript的通用过滤器。应该有一种使用范围和多选过滤器的方法。 但我是OOP新手,不知道应该如何使用类来实现。 对后端的筛选器请求应如下所示,如果存在新类型,则必须有一种方法来扩展它们:

[
  { field: 'id', operator: '=', value: 12 },
  'OR',
  { field: 'name', operator: '=', value: 'testName' },
  'OR',
  [
    { field: 'age', operator: '>=', value: '20' },
    'AND',
    { field: 'age', operator: '=<', value: '30' }
  ]
]
FilterParameters类如下所示,但它不可扩展:

export class FilterParameters {
    filter: (string | object | number)[]
    constructor() {
        this.filter = []
    }
    createFilter(receivedFields: (object)[]) {
        let filterArr:(object)[] = []
        let defaultFilter = receivedFields.filter((item)  => item.type === 'default')
        let rangeFilter = receivedFields.filter(item => item.type === 'range')
        defaultFilter.forEach((item:any) => {
            let defaultFilterObj = {
                field: item.field,
                operator: '=',
                value: item.value
            }
            filterArr.push(defaultFilterObj)
        })
        rangeFilter.forEach((item:any) => {
            let conditionArr = []
            let startCondition = item.value.split(' ')[0]
            let endCondition = item.value.split(' ').pop()
            let objStartCondition = {
                field: item.field,
                operator: '>=',
                value: startCondition
            }
            let objEndCondition = {
                field: item.field,
                operator: '=<',
                value: endCondition
            }
            conditionArr.push(objStartCondition)
            conditionArr.push('AND')
            conditionArr.push(objEndCondition)
            filterArr.push(conditionArr)
        })
        this.filter = filterArr.map((e, i) => i < filterArr.length - 1  ? [e, 'OR'] : [e]).reduce((a, b) => a.concat(b))
        return this.filter
    }
}
导出类过滤器参数{
过滤器:(字符串|对象|编号)[]
构造函数(){
this.filter=[]
}
createFilter(receivedFields:(对象)[]){
让filterArr:(对象)[]=[]
让defaultFilter=receivedFields.filter((项)=>item.type==='default')
让rangeFilter=receivedFields.filter(item=>item.type==='range')
defaultFilter.forEach((项目:任意)=>{
设defaultFilterObj={
字段:item.field,
运算符:'=',
值:item.value
}
filterArr.push(默认FilterObj)
})
rangeFilter.forEach((项目:任意)=>{
让条件arr=[]
让startCondition=item.value.split(“”)[0]
让endCondition=item.value.split(“”).pop()
设objStartCondition={
字段:item.field,
运算符:“>=”,
值:startCondition
}
设objEndCondition={
字段:item.field,

运算符:'=如果我从头开始设计,我会改变很多事情,因为您当前的方法有很多限制。例如,输入数组总是使用
”或“
”,无法使用
”和“
”或创建分组

我假设您的输入格式和输出格式是固定的,我们的任务只是创建一种从输入映射到输出的更可扩展的方式

输入的每个过滤器都是这样的,我们希望收到一个它们的数组

interface InputFilter {
    field: string;
    type: string;
    value: any;
}
由于嵌套,输出更加复杂

interface OutputFilterElement {
    field: string;
    operator: string;
    value: any;
}

type Joiner = 'OR' | 'AND';

type OutputFilterArray = Array<OutputFilterElement | Joiner | OutputFilterArray>

type OutputFilter = OutputFilterElement | OutputFilterArray;
我们希望我们的
FilterParameters
类只知道
TypeParser
接口,而不知道或关心该接口的具体实现。因此,我们可以通过将任意数量的
TypeParser
对象作为参数传递给
构造函数来实例化该类

该类有一个公共方法
parse
,用于根据可用的类型解析器解析输入筛选器数组。它查找匹配的解析器,然后委托实际的解析

export class FilterParameters {
    private readonly typeParsers: TypeParser[];

    /**
     * construct an isntance of the FilterParser by passing the TypeParsers to handle specific types
     */
    constructor(...typeParsers: TypeParser[]) {
        this.typeParsers = typeParsers;
    }

    /**
     * helper function parses each element of the array
     * might throw an error
     */
    private parseOne(input: InputFilter): OutputFilter {
        // find the parser for this type
        const parser = this.typeParsers.find(p => p.type === input.type);
        if (!parser) {
            throw new Error(`no filter found for type ${input.type}`);
        }
        return parser.parse(input);
    }

    /**
     * handle an array of individual filters by assuming an 'OR' relationship
     */
    public parse(input: InputFilter[]): OutputFilter {
        // use flatMap to insert 'OR' between elements
        return input.flatMap(
            (condition, i) => {
                const parsed = this.parseOne(condition);
                return i === 0 ? [parsed] : ['OR', parsed]
            }
        );
        // do we want to catch errors here? or allow them to be thrown?
    }
}
如果类不需要任何类型的实例,那么我认为使用类是没有意义的,因此我将创建
TypeParser
的具体实现,作为实现接口的对象

const EqualityParser: TypeParser = {
    type: 'default',
    parse: ({ field, value }: InputFilter): OutputFilter => {
        return ({
            field,
            // operator is always equals for default filter
            operator: '=',
            value
        });
    }
}

const RangeParser: TypeParser = {
    type: 'range',
    parse: ({ field, value }: InputFilter): OutputFilter => {
        // split based on hyphen with optional spaces before and after
        const [min, max] = value.split(/\s*-\s*/);
        // are these always numbers? should we use parseFloat?
        return ([{
            field,
            operator: '>=',
            value: min
        },
            'AND',
        {
            field,
            operator: '<=',
            value: max
        }]);
    }
}

如果我从头开始设计,我会改变很多事情,因为您当前的方法有很多限制。例如,输入数组总是使用
'或'
,无法使用
'和'
或创建分组

我假设您的输入格式和输出格式是固定的,我们的任务只是创建一种从输入映射到输出的更可扩展的方式

输入的每个过滤器都是这样的,我们希望收到一个它们的数组

interface InputFilter {
    field: string;
    type: string;
    value: any;
}
由于嵌套,输出更加复杂

interface OutputFilterElement {
    field: string;
    operator: string;
    value: any;
}

type Joiner = 'OR' | 'AND';

type OutputFilterArray = Array<OutputFilterElement | Joiner | OutputFilterArray>

type OutputFilter = OutputFilterElement | OutputFilterArray;
我们希望我们的
FilterParameters
类只知道
TypeParser
接口,而不知道或关心该接口的具体实现。因此,我们可以通过将任意数量的
TypeParser
对象作为参数传递给
构造函数来实例化该类

该类有一个公共方法
parse
,用于根据可用的类型解析器解析输入筛选器数组。它查找匹配的解析器,然后委托实际的解析

export class FilterParameters {
    private readonly typeParsers: TypeParser[];

    /**
     * construct an isntance of the FilterParser by passing the TypeParsers to handle specific types
     */
    constructor(...typeParsers: TypeParser[]) {
        this.typeParsers = typeParsers;
    }

    /**
     * helper function parses each element of the array
     * might throw an error
     */
    private parseOne(input: InputFilter): OutputFilter {
        // find the parser for this type
        const parser = this.typeParsers.find(p => p.type === input.type);
        if (!parser) {
            throw new Error(`no filter found for type ${input.type}`);
        }
        return parser.parse(input);
    }

    /**
     * handle an array of individual filters by assuming an 'OR' relationship
     */
    public parse(input: InputFilter[]): OutputFilter {
        // use flatMap to insert 'OR' between elements
        return input.flatMap(
            (condition, i) => {
                const parsed = this.parseOne(condition);
                return i === 0 ? [parsed] : ['OR', parsed]
            }
        );
        // do we want to catch errors here? or allow them to be thrown?
    }
}
如果类不需要任何类型的实例,那么我认为使用类是没有意义的,因此我将创建
TypeParser
的具体实现,作为实现接口的对象

const EqualityParser: TypeParser = {
    type: 'default',
    parse: ({ field, value }: InputFilter): OutputFilter => {
        return ({
            field,
            // operator is always equals for default filter
            operator: '=',
            value
        });
    }
}

const RangeParser: TypeParser = {
    type: 'range',
    parse: ({ field, value }: InputFilter): OutputFilter => {
        // split based on hyphen with optional spaces before and after
        const [min, max] = value.split(/\s*-\s*/);
        // are these always numbers? should we use parseFloat?
        return ([{
            field,
            operator: '>=',
            value: min
        },
            'AND',
        {
            field,
            operator: '<=',
            value: max
        }]);
    }
}

为什么要转换数组?如何表示
[{field:'id',operator:'12}'和',{field:'name',operator:'testName'}]
[{field:'percentage',operator:'>',value:'20.123'}和',{field:'percentage',operator:'IMHO此类筛选器的简单表示形式是二叉树,如
{OR:[{field:'id',operator:'=',value:12},{OR:[{field:'name',operator:'=',value:'testName'}和:[{field:'age',operator:'>=',value:'20'},{field age:'age',operator:'=',value:'20'}]
。此筛选器的类型是
接口或{Or:[filter,filter];}
接口和{And:[filter,filter];}
类型筛选器=或|和|{field:string;运算符:string;值:string | number;};
我需要一个数组
[{field:'name',type:'default',value:'yulya'},{field:'age',type:'range',value:'20-30'}]
并将其转换为
[{field:'id',运算符:'=',value:12}',OR',{field:'name',运算符:'=',value:'testName'},'或',[{field:'age',operator:'>=',value:'20'},'和',{field:'age',operator:'''=指定了优先级吗?后端对
[{field:'id',operator:'=',value:12}'和',{field:'name',operator:'=',value:'testName'}或',{field age:'age',operator:'>=',value:'20}做了什么
?第一个
或第一个
或从左到右或从右到左?为什么要转换数组?如何表示
[{字段:'id',运算符:'=',值:12}