# The problem

The built in Array.filter method cannot be used to filter array items in a type safe way. To exclude some items on the type level we would need a typecast. Are there any better alternatives?

To demonstrate this, let’s say we have a basket of apples and oranges. We want to make some fresh orange juice. And of course all that using TypeScript.

Our first approach might look like this:

Note that this example uses type literals extensively to make it more concise.

Looks great, however, TypeScript will not allow to use squeezeJuice like that. Instead, we will get a type error:

Why? basket.filter filtered apple values just fine, however this method always returns an array of the same type as its input. So even that apples are filtered on the value level, on the type level exactly the same array type is returned.

A little typecast can “solve” the situation.

However, typecasts are bad for your code. Eg. in this case, if we used invalid filtering function we wouldn’t known until the program runs. An apple would got through to the squeezer and break the device. No more fresh juice!

How can we improve this situation?

# Refining an array

The built-in filter didn’t suit our needs, so let’s try to implement a type-safe filter ourselves.

Also, let’s use type guards because we want to feel smart today.

Wait, but what are type guards?

In short, type guard is a function that can assert if its input argument is of specific type. For example, (x: unknown): x is number => typeof x === "number" is a super simple example of a type guard. It returns a boolean (typeof x === "number" is true or false) and its return type signature containes extra type level information (x is number) which can be used by the compiler after the function call. In this case if it returns true that means that type of x is a number. Please read TypeScript documentation for further details.

Our refineArray function seems to work, however, note the typeguard

is a bit explicit. It requires us to provide the exact type we would like the array to have, without any helpful type inference. So uncivilized.

refineArray is usable in this simple scenario, however it can get cumbersome to declare its type guard signature with complex or generic types. Also that’s another area for possible human error.

# One step back, two steps forward

Let’s try a different approach. Maybe a sibling of filter function, map could help us?
map maps returned array on a type level as well, seems promising. So what if instead of filtering we could map values that we don’t want to kind of a no value?

Our first approach could be to use undefined as a no value:

That leaves us with a bit simpler array but still a heterogeneous one since we’ve introduced a new type undefined. We could filter this array and typecast the result but this is yet another step back.

So can we use map without introducing any other types that input arguments?

What about using an empty array to represent no value? To stay consistent a valid result could return an array with a single item.

Almost there. Result is not exactly what we wanted but looks promising. Now we only need a function that would turn an Array<Array<T>> into an Array<T>. Meet flat:

It turns out, this use case of mapping each item into an array and joining results is so common that it has its own built-in method - flatMap. Let’s try it:

# Summary

You can use .flatMap for type safe filtering.

Pros:

• Works great with type inference - no need for typecasts or explicit typings.
• It’s built in, no need for custom code or libraries.
• No space for human error on type level as compared to .filter.

Cons:

• It’s a bit less readable than plain .filter.

# Discussion

If TypeScript was able to infer type of type guard functions, refineArray would be equally or even more user friendly than flatMap for the sake of filtering. What is more, then the built-in Array.filter method could use the same typings as we declared in refineArray so that we would get a best of all worlds - a built-in, type safe, readable filtering function.

TL;DR: You can use flatMap as a built-in type-safe method to filter heterogeneous arrays.