3

I'm new to Angular.

How can I solve this problem?

I have installed Angular CLI: 11.0.7 and Node: 12.18.4

I have installed an Angular route guard:

ng g guard auth --skip-tests

The error:

Error: src/app/_guards/auth.guard.ts:15:5 - error TS2322: Type 'Observable<true | undefined>' is not assignable to type 'Observable'. Type 'boolean | undefined' is not assignable to type 'boolean'. Type 'undefined' is not assignable to type 'boolean'.

 15     return this.accountService.currentUser$.pipe(
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 16       map(user => {
    ~~~~~~~~~~~~~~~~~~~
...
 19       })
    ~~~~~~~~
 20     )
    ~~~~~
src/app/_guards/auth.guard.ts:16:11 - error TS7030: Not all code paths return a value.

16       map(user => {
             ~~~~~~~~~

guard

The Codes:

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree } from '@angular/router';
import { ToastrService } from 'ngx-toastr';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { AccountService } from '../_services/account.service';

@Injectable({
  providedIn: 'root'
})
export class AuthGuard implements CanActivate {
  constructor(private accountService: AccountService, private toastr: ToastrService) {}

  canActivate(): Observable<boolean> {
    return this.accountService.currentUser$.pipe(
      map(user => {
        if (user) return true; // the problem occurs here!
        this.toastr.error('You shall not pass!')
      })
    )
  }
  
}
x19
  • 8,277
  • 15
  • 68
  • 126

3 Answers3

6

Possibly because nothing is returned from the map operator when user is undefined. Try to return something

export class AuthGuard implements CanActivate {
  constructor(private accountService: AccountService, private toastr: ToastrService) {}

  canActivate(): Observable<boolean> {
    return this.accountService.currentUser$.pipe(
      map(user => {
        if (user) return true;
        this.toastr.error('You shall not pass!');
        return false;
      })
    )
  }
}

Or better yet, you could use tap operator instead of map to perform side-effects like the 'toastr' notification.

export class AuthGuard implements CanActivate {
  constructor(private accountService: AccountService, private toastr: ToastrService) {}

  canActivate(): Observable<boolean> {
    return this.accountService.currentUser$.pipe(
      tap(user => {
        if (!user) this.toastr.error('You shall not pass!')
      })
    )
  }
}
ruth
  • 29,535
  • 4
  • 30
  • 57
0

You can use a double-not operator to return the value. It's important to emit a value in both cases with a definitive value, either true or false.

      map(user => {
        if (!user) {
          this.toastr.error('You shall not pass!')
        }
        return !!user;
      })
    )
James
  • 86
  • 1
  • 7
0

I had similar issue. That because Typescript enforced some typing in newest version. When you get :

Error: src/app/_guards/auth.guard.ts:15:5 - error TS2322: Type 'Observable<true | undefined>

It means you try to return NOT BOOLEAN - in that case undefined - from guard which has specific list of types it can return. That is : Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree. As you can see ther is no UNDEFINED at all. You did not specified return for all 'branches'. That is in your case. Simply saying return true or false real clear.

  map(user => {
    if (user) return true; // HERE YOU RETURNED TRUE 
    this.toastr.error('You shall not pass!')
    // HERE YOU DID NOT RETURNED return false;
    // from where interpreter should know what should be return 
    // when user is FALSE? ... it is not specified. Isn't it ?
  })

There is also one more issue in that training on Udemy. When you define your this.accountService.currentUser$ it may be undefined case. Newest Typescript do not like unclear types - what is actually good thing.

In my case I put wrong types on this.accountService.currentUser$ to User | null.

  private currentUserSource = new ReplaySubject<User | null>(1);
  currentUser$ = this.currentUserSource.asObservable();

Null did that I received USER or NULL which in condition !null caused it did not enter condition. Exactly same situation would be with undefined. My solution was following:

       canActivate(): Observable<boolean>  {
        return this.accountService.currentUser$.pipe(
          tap(user => { 
            if (user === null) this.toastr.error('You shall not pass!');
   // your case should be:
  // if (user === undefined) this.toastr.error('You shall not pass!');
          }),
          map((user: any) => {
           return user ? true : false;
          })
    
        );

  }
PPik
  • 11
  • 4