RXJS how to handle returned boolean or Observable<boolean> in switchmap

Viewed 456

For context I've got two guards on an angular route but I can't have them both on the route canActivate due to side effects in one and the fact that all guards get checked on a route regardless.

So to workaround I have one guard that calls the others canActivate in a certain case:

canActivate(
        next: ActivatedRouteSnapshot,
        state: RouterStateSnapshot
    ): Observable<boolean> | Promise<boolean> | boolean {
        return this.service.state$.pipe(
            filter((s) => s.updatedFromServer),
            switchMap((s) => {
                if (/* a condition that means this guard is satisfied */) {
                    return this.otherGuard.canActivate(next, state);
                } else {
                    return false;
                }
            })
        );

The other guard's canActivate return type is also Observable<boolean> | Promise<boolean> | boolean and similarly can return an actual boolean or Observable.

The TS error complaint is now:

Type 'false | Observable<boolean>' is not assignable to type 'ObservableInput<any>'.
Type 'false' is not assignable to type 'ObservableInput<any>'

If the other guard returns just an Observable and the switchmap return here is also changed to of(false) then it seems ok, but I would prefer to retain the canActivate signatures where a resolved boolean can be returned - I just don't know how to do that within the context of a rxjs map.

1 Answers

You can check if the result is an Observable or Promise with isObservable and isPromise. If not wrap the value with of.

import { isObservable } from 'rxjs';
import { isPromise } from 'rxjs/internal-compatibility';

return this.service.state$.pipe(
  filter((s) => s.updatedFromServer),
  switchMap((s) => {
    let result: any;
    if (/* a condition that means this guard is satisfied */) {
      result = this.otherGuard.canActivate(next, state);
    } else {
      result = false;
    }
    return isObservable(result) || isPromise(result) ? result : of(result)
  })
);
Related