30

Use type predicate with filter for type safety

Friday, October 30, 2020

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

Do

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.

Avoid

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.

Why

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',
);
Vojtech Mašek

I am a software engineer focusing on front-end with expertise in Angular and TypeScript. I'm with Angular since its alpha phase. I'm also a Head of engineering and co-founder of FlowUp, a software development agency, where I lead multiple agile development teams.

Discussions are healthy ❤️