Angular6升级至7后Observable类型不匹配编译错误求助
Hey there, let's break down why this TypeScript error popped up after your Angular upgrade and how to fix it quickly.
The error Type 'Observable<{}>' is not assignable to type 'Observable<boolean>' happens because TypeScript's type inference got stricter in the version bundled with Angular 7 (TS 3.x vs Angular 6's TS 2.9). Your executeGuardsCanActivate method is returning an observable whose type TypeScript can't reliably infer as Observable<boolean> anymore—especially when you chain guards using forEach and mergeMap.
Here's how to fix it step by step:
1. Add explicit type annotations to executeGuardsCanActivate
First, tell TypeScript exactly what this method accepts and returns. We'll:
- Define the
guardsparameter as an array ofCanActivatetypes - Explicitly set the return type to
Observable<boolean>
You'll need to import Type from @angular/core for this.
2. Replace forEach with reduce (for better type safety)
Using reduce to build your observable chain helps TypeScript track the type consistently through each iteration, unlike forEach which can cause type widening.
Modified Full Guard Code
import { of as observableOf, Observable } from 'rxjs'; import { mergeMap } from 'rxjs/operators'; import { Injectable, Injector, Type } from '@angular/core'; import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, CanActivateChild } from '@angular/router'; @Injectable() export class MasterGuard implements CanActivate, CanActivateChild { constructor(private readonly injector: Injector) {} canActivate( next: ActivatedRouteSnapshot, state: RouterStateSnapshot ): Observable<boolean> | Promise<boolean> | boolean { // Use optional chaining to safely check guard existence (supported in TS 3.1+) if (next.data.guards?.canActivate?.length > 0) { return this.executeGuardsCanActivate(next.data.guards.canActivate, next, state); } else { return true; } } canActivateChild( next: ActivatedRouteSnapshot, state: RouterStateSnapshot ): Observable<boolean> | Promise<boolean> | boolean { // Reuse canActivate logic for child routes return this.canActivate(next, state); } executeGuardsCanActivate( guards: Array<Type<CanActivate>>, next: ActivatedRouteSnapshot, state: RouterStateSnapshot ): Observable<boolean> { // Use reduce to build the observable chain with consistent typing return guards.reduce((currentObs, guard) => { return currentObs.pipe( mergeMap(isAllowed => { if (isAllowed) { const guardInstance = this.injector.get(guard); return guardInstance.canActivate(next, state); } else { return observableOf(false); } }) ); }, observableOf(true)); } }
Why This Works:
- Explicit Types: By defining
guards: Array<Type<CanActivate>>, TypeScript knows every guard in the array has acanActivatemethod that returns a boolean-compatible type (Observable<boolean> | Promise<boolean> | boolean). - Reduce Instead of ForEach:
reducemaintains the type of the initial observable (Observable<boolean>) through each mergeMap operation, so TypeScript never loses track of the expected return type. - Optional Chaining: The
?.operator prevents errors ifnext.data.guardsorcanActivateis undefined, which is a nice safety upgrade supported in Angular 7's TypeScript version.
If you prefer to keep using forEach instead of reduce, you can still fix the error by adding an explicit type annotation to the start variable:
let start: Observable<boolean> = observableOf(true);
This tells TypeScript to enforce that start always stays an Observable<boolean>, even through the forEach iterations.
内容的提问来源于stack exchange,提问作者Ashutosh Singh

