Handling combinations of optional parameters for an Angular filter pipe
Direction

My approach would be based on the Strategy design pattern. Each of the original if-elseif-elseif-... sections is now becoming an IRule object that represents the rule. This object does two things:

A) It defines whether it may or may not be applied to the given filter input;

B) It defines the application function (logic) itself.

In order to select a proper rule dynamically in run time, we need a registry of the rules which is implemented as a simple array.

Benefits

Implementation of a new "rule" becomes almost trivial: we'll need to create a rule object itself, and add it to a proper place in the list of registry entries, so that it's picked up.

Code

import { Pipe, PipeTransform } from "@angular/core";

const ALL = "Show all";
const BLANK = "";

/**
 * Represents a contract of a single rule.
 */
interface IRule {
    /**
     * A predicate that sayw whether the rule may or may not be applied to a value based on the filter.
     */
    isApplicableTo(filters: Object[]): boolean;

    /**
     * Implementation of rule applied to a value based on filter parameters.
     */
    applyTo(filters: Object[], value: any): boolean;
}

/**
 * The complete list of rules that your system supports.
 * NOTICE that the order of the rule is important.
 * If more than one rule is applicable to a filters object,
 * the very first applicable rule is applied.
 */
const RULES_REGISTRY: IRule[] = [
    new AllMatchingRegexRule(),
    new MatchingStatusRule(),
    new SomeMatchingRegexRule()
];

/**
 * Three sample rules based on your original code.
 */

class AllMatchingRegexRule implements IRule {
    isApplicableTo(filters: Object[]): boolean {
        const safeFilters = getSafeFilters(filters);
        return isAllFilter(safeFilters) &amp;&amp; isNotBlankFilter(safeFilters);
    }

    applyTo(filters: Object[], value: any): boolean {
        return descriptionRegexMatched(filters[1], value);
    }
}

class MatchingStatusRule implements IRule {
    isApplicableTo(filters: Object[]): boolean {
        const safeFilters = getSafeFilters(filters);
        return isNotAllFilter(safeFilters) &amp;&amp; isBlankFilter(safeFilters);
    }

    applyTo(filters: Object[], value: any): boolean {
        return statusMatches(filters, value);
    }
}

class SomeMatchingRegexRule implements IRule {
    isApplicableTo(filters: Object[]): boolean {
        const safeFilters = getSafeFilters(filters);
        return isNotAllFilter(safeFilters) &amp;&amp; isNotBlankFilter(safeFilters);
    }

    applyTo(filters: Object[], value: any): boolean {
        return value.status === filters[0] &amp;&amp; descriptionRegexMatched(filters[1], value);;
    }
}

// Reusable working horse lambdas (use in Rules)

const getSafeFilters = (filters: Object[]) =&gt; filters &amp;&amp; filters.length &gt;= 2 ? filters : new Object[2];

const descriptionRegexMatched = (pattern: any, value: any) =&gt; new RegExp(pattern, 'gi').test(value['part']['description']);

const statusMatches = (filters: Object[], value: any) =&gt; value.status === filters[0];

const isAllFilter = (filters: Object[]) =&gt; filters[0] === ALL;
const isNotAllFilter = (filters: Object[]) =&gt; !isAllFilter(filters);

const isBlankFilter = (filters: Object[]) =&gt; filters[1] === BLANK;
const isNotBlankFilter = (filters: Object[]) =&gt; !isBlankFilter(filters);


Consuming code (that uses the Rule registry)

Now your filter can be implemented like this:

@Pipe({ name: 'filter' })
export class FilterPipe implements PipeTransform {

    transform(values: any, type: string, ...filters: Object[]): any {
        switch (type) {
            case 'MAINTENANCE_EVENT':
                if (filters.length !== 2) {
                    throw new SyntaxError('MAINTENANCE_EVENT requires exactly 2 filter parameters: &lt;string&gt;status and &lt;string&gt;description')
                }

                if (isAllFilter(filters) &amp;&amp; isNotBlankFilter(filters)) {
                    return values;
                } else {
                    return values.filter(value =&gt; {
                        const ruleToApply = RULES_REGISTRY.find(rule =&gt; rule.isApplicableTo(filters));

                        if (!ruleToApply) {
                            throw new Error("Attempting to filter by properties not present in available &lt;MaintenanceEvent&gt;s");
                        }

                        return ruleToApply.applyTo(filters, value);
                    });
                }

            default:
                return values;
        }
    }
}


Disclaimer


I am very new to TypeScript so I may be using wrong idioms. The key in my answer is the design change, rather than implementation details.


P.S.

Notice that the same approach may be applied to type == 'MAINTENANCE_EVENT' 'OTHER_EVENT' .... And, speaking more generally, Strategy design pattern is almost always a working alternative to an if or switch. Of course, to get the benefits of the pattern, we need to write some boilerplate code (IRule + Rule registry), however it's a great trade of for the scenarios where there are a lot of rules, or the rules are frequently added/modified, or the rule selection strategy may change in run time.