Use type predicate with filter for type safety
Friday, October 30, 2020
Use type predicate with filter for type safety
Type predicates of arrow functions are very usefull feature of a TypeScript. They have been around for a while (since version ~3.5). They are exceptinally helpful when using filter functions/operators that can manipulate what data will pass through and thus alternate the return type in process.
In TypeScript people will sometimes encounter the error that says that some type is not assignable to some other type in your RxJS chain or when working with Arrays. This is usually caused by impropper typing of a filter return type. Filter does not understand the logic you have in the function you pass in, so you have to tell it explicitly what is the expected result of that statement. This has side benefits of documenting your code with code which is the best kind of documentation as it stays fresh as long as code is used and up to date.
Instructions
Specify what return type do you expect to pass through the filters. It is as easy as describing which parameter acts as the subject and what is the expected result of filtering.
Do not explicitly cast or hard code the type in the place where it shows the error, it can lead to actual runtime errors in your code if you override the type safety with your explicit statement or even worse typing it as any.
It will bring type safety to you RxJS chains or array manipulations, act as a documentation, increase readability and get rid of annoying type error that you'd expect to not be there.
Code Examples
Use type predicate
// RxJS pipe filter
route.queryParamMap.pipe(
map(params => params.get('id')),
filter((id) => typeof id === 'string'),
switchMap(id => this.http.get('/api', { params: { id: id as string } })),
).subscribe(id => console.log(id));
// Array filter
const arrayOfNumbers: number[] = ['foo', 'bar', 42, 1337, 'wololo'].filter(
(item): item is number => typeof item === 'number',
);
Casting explicit type or using any
// RxJS pipe filter
route.queryParamMap.pipe(
map(params => params.get('id')),
filter((id): id is string => typeof id === 'string'),
switchMap(id => this.http.get('/api', {params: {id}}))
).subscribe(id => console.log(id));
// Array filter
const arrayOfRandomStuff: (string | number | null)[] = ['foo', 'bar', 42, 1337, 'wololo'].filter(
(item) => typeof item === 'number',
);
Have a question or comment?