2
votes

I have a service in my Angular app with the following constructor:

 constructor(private router: Router, private titleService: Title) {
      this.activeMenuItem$ = this.router.events.pipe(
          filter(event => event instanceof NavigationEnd),
          map(route => {
            let active = new MenuItem('test', route.url, null);
            return active;
        })
      );
  }

where activeMenuItem$ is an Observable. It works fine on the browser but I'm getting a compilation error:

ERROR in src/app/service/app-menu.service.ts(26,53): error TS2339: Property 'url' does not exist on type 'Event'. Property 'url' does not exist on type 'ActivationEnd'.

But if I try a cast this way:

 constructor(private router: Router, private titleService: Title) {
      this.activeMenuItem$ = this.router.events.pipe(
          filter(event => event instanceof NavigationEnd),
          map(route => {
            let routeNav = route as NavigationEnd;
            let active = new MenuItem('test', routeNav.url, null);
            return active;
        })
      );
  }

It works perfectly!.

can anyone explain to me why? Tks

1

1 Answers

3
votes

Using just filter(event => event instanceof NavigationEnd) isn't a type guard for TypeScript.

In other words, TypeScript doesn't know that after this condition map(route) will always be map(route: NavigationEnd).

However, you can hint TypeScript with a typeguard like this:

filter((event): event is NavigationEnd => x instanceof NavigationEnd),

Then your map(event => ...) is equivalent to map((event: NavigationEnd) => ...).

Live demo: https://stackblitz.com/edit/rxjs-wtwuew

More in-depth description: https://medium.com/ngconf/filtering-types-with-correct-type-inference-in-rxjs-f4edf064880d