Search⌘ K
AI Features

Crafting a Generic Filter Function

Explore how to develop a generic filter function in TypeScript and create dynamic React components to apply flexible filtering. Understand handling edge cases like empty strings, null, and undefined values by leveraging JavaScript's boolean coercion and array methods for effective data filtering.

It’s time to create the final generic functionality in this course—generic filtering. Filtering is like searching in that we’ll utilize JavaScript’s built-in filter function, but our predicate function will be more complex.

Filtering vs. Searching and Sorting

In some ways, filtering isn’t as intuitive as search or sort. For example, let’s consider a property that is of type string. There are numerous edge cases we have to consider. For example, what do we do with properties that have any empty string value, like ""? What do we do if the value is null? What if it’s undefined? There are a few options available to us. We could remove these empty null and undefined values entirely or perhaps choose to keep the empty string values ("") and remove only null or undefined values. These choices will depend on the specific use case of what we are building.

Let’s start with the signature of our new genericFilter() function.

TypeScript 3.3.4
export const genericFilter = <T>(object: T, properties: Array<keyof T>) => {
}

To build flexibility into our generic filter, we’ll rely on JavaScript’s ability to coerce a type to boolean. This means letting JavaScript determine what values should be evaluated as true or false.

We’ll facilitate this by putting the value in question into a boolean context. Boolean context simply means that we use a variable in an if statement or with a ternary operator. A ternary operator is a syntax shorthand for if/else logic, using the ? and : characters—for example, isTrue ? console.log("true!") : console.log("false!")).

In either of these situations, JavaScript needs to first coerce the value to its boolean equivalent. In the least amount of code, that can be accomplished using the ternary operatorA syntax shorthand for if / else logic, using the ? and : characters. Example: isTrue ? console.log("true!") : console.log("false!"), where we will simply ask if the passed object with the given key evaluates to true or not, i.e.:

TypeScript 3.3.4
return object[property] ? true : false

The every() array function

We’ll use one of JavaScript’s built-in array functions, the every() function, to evaluate the values. Unlike some, which will evaluate as true if even one element passes the predicate function, every() requires that all elements passed to the predicate function return as true.

Tip: Remember that a predicate function is just a function that has a boolean return value: true or false. For JavaScript array functions like filter(), some(), and every(), the function must be a predicate function.

After we wrap our ternary statement around an every() call on properties, our genericFilter() function is complete.

TypeScript 3.3.4
export const genericFilter = <T>(object: T, properties: Array<keyof T>): boolean => {
return properties.every(property => {
return object[property] ? true : false
})
}

Coercing a JavaScript type to boolean

The following table shows what values evaluate to true in a boolean context:

Boolean context type coercion in JavaScript

Value

Coercion to bool

true

true

false

false

0

false

''

false

null

false

undefined

false

NaN

false

Infinity

false

[]

false

{}

false

This is a decent naïve implementation. The empty string example we discussed above ("") evaluates as false and will be filtered out by our current implementation.

But we have to be careful! For example, in this situation, 0 evaluates as false. That means that if we want to show all widgets that don’t have an id, a widget with an id of 0 will show up as a false positive. In other words, according to JavaScript, an id with a value of 0 is falsy. So again, we might choose instead only explicitly to filter null and undefined values. A generic filter function like that would look like this:

TypeScript 3.3.4
export const genericFilter = <T>(object: T, properties: Array<keyof T>): boolean => {
return properties.every(property => {
return object[property] !== null && object[property] !== undefined
})
}