How to use type predicates when using Array.filter with arrow function

I have problem with using type predicates with arrow function.

// this working good 
function filterUndefined1<T>(x: T | undefined): x is T {
  return !x;
}
[1, undefined, 3, 5, undefined].filter(filterUndefined1);


// Retrun Type Error
type FilterUndefined2 = <T>(x: T | undefined) => x is T;
const filterUndefined2: FilterUndefined2 = (x: string) => !x;

// What I want to do
[1, undefined, 3, 5, undefined].filter(filterUndefined2);

// this working good
[1, undefined, 3, 5].filter(<T>(z: T | undefined): z is T => !z);

Why does my arrow function cause a type error when it leaves the filter argument?

>Solution :

All the is T assertions are lying, the code ensures that the values are falsy instead.

To fix the filterUndefined2 declaration the corresponding type’s generic parameter should be moved and the argument has to match the type; string does not match T | undefined.

type FilterUndefined2<T> = (x: T | undefined) => x is undefined;
const filterUndefined2: FilterUndefined2<string> =
    (x: string | undefined): x is undefined => !x;

This is still wrong for the usage with the example array, though, because that does not contain strings. Hence, this would work:

type FilterUndefined2<T> = (x: T | undefined) => x is undefined;
const filterUndefined2: FilterUndefined2<number> =
    (x: number | undefined): x is undefined => !x;
console.log([1, undefined, 3, 5, undefined].filter(filterUndefined2));

If you actually want to keep the values, which would make more sense, you can invert again using ! or just do something like x != null.

type FilterUndefined2<T> = (x: T | undefined) => x is T;
const filterUndefined2: FilterUndefined2<number> =
    (x: number | undefined): x is number => !!x;
console.log([1, undefined, 3, 5, undefined].filter(filterUndefined2));

Leave a Reply