Skip to content
Alexander Opalic
Alexander Opalic

Better Type Inference with TypeScript Array Filters

How to improve TypeScript type inference when filtering arrays using type guards

Today I learned about the difference between using Boolean as a filter function versus using a proper type guard for array filtering in TypeScript.

While Boolean can remove falsy values, it doesn’t improve TypeScript’s type inference:

const const numbers: (number | null | undefined)[]numbers = [1, null, 2, var undefinedundefined, 3].Array<number | null | undefined>.filter(predicate: (value: number | null | undefined, index: number, array: (number | null | undefined)[]) => unknown, thisArg?: any): (number | null | undefined)[] (+1 overload)
Returns the elements of an array that meet the condition specified in a callback function.
@parampredicate A function that accepts up to three arguments. The filter method calls the predicate function one time for each element in the array.@paramthisArg An object to which the this keyword can refer in the predicate function. If thisArg is omitted, undefined is used as the this value.
filter
(var Boolean: BooleanConstructorBoolean);
const
const type: (number | null | undefined)[]
type
= const numbers: (number | null | undefined)[]numbers;

The type remains (number | null | undefined)[] because TypeScript doesn’t understand that we’re removing null and undefined values.

However, using a type guard provides proper type inference:

const const numbersTyped: number[]numbersTyped = [1, null, 2, var undefinedundefined, 3].Array<number | null | undefined>.filter<number>(predicate: (value: number | null | undefined, index: number, array: (number | null | undefined)[]) => value is number, thisArg?: any): number[] (+1 overload)
Returns the elements of an array that meet the condition specified in a callback function.
@parampredicate A function that accepts up to three arguments. The filter method calls the predicate function one time for each element in the array.@paramthisArg An object to which the this keyword can refer in the predicate function. If thisArg is omitted, undefined is used as the this value.
filter
(
(num: number | null | undefinednum): num: number | null | undefinednum is type NonNullable<T> = T & {}
Exclude null and undefined from T
NonNullable
<typeof num: number | null | undefinednum> => num: number | null | undefinednum !== null && num: number | undefinednum !== var undefinedundefined
); const
const type: number[]
type
= const numbersTyped: number[]numbersTyped;

Now TypeScript correctly infers the type as number[]!

Key takeaways:

  • Use type guards instead of Boolean for better type inference
  • NonNullable<T> is a useful utility type for removing null and undefined
  • Type predicates (is keyword) help TypeScript understand our filtering logic
#typescript #arrays #type-inference