Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic type 'CanDeactivate<T>' requires 1 type argument(s)

In the code below (an example from the book "Angular 2 Development with TypeScript"):

import {CanDeactivate, Router} from "@angular/router";
import {Injectable} from "@angular/core";

@Injectable()
export class UnsavedChangesGuard implements CanDeactivate{

    constructor(private _router:Router){}

    canDeactivate(){
        return window.confirm("You have unsaved changes. Still want to leave?");

    }
}

I see a warning when I hover over CanDeactivate in WebStorm:

enter image description here

Referring to the answers to this question - Generic type 'Observable<T>' requires 1 type argument - the following change removes the warning:

export class UnsavedChangesGuard implements CanDeactivate<any>{

However, I would like to know how I can find out what is the actual argument that CanDeactivate requires.

Edit: Looking at @angular/router/src/interfaces.d.ts we can see the following:

/**
 * @whatItDoes Indicates that a class can implement to be a guard deciding if a route can be
 * deactivated.
 *
 * @howToUse
 *
 * ```
 * class UserToken {}
 * class Permissions {
 *   canDeactivate(user: UserToken, id: string): boolean {
 *     return true;
 *   }
 * }
 *
 * @Injectable()
 * class CanDeactivateTeam implements CanDeactivate<TeamComponent> {
 *   constructor(private permissions: Permissions, private currentUser: UserToken) {}
 *
 *   canDeactivate(
 *     component: TeamComponent,
 *     route: ActivatedRouteSnapshot,
 *     state: RouterStateSnapshot
 *   ): Observable<boolean>|Promise<boolean>|boolean {
 *     return this.permissions.canDeactivate(this.currentUser, route.params.id);
 *   }
 * }
 *
 * @NgModule({
 *   imports: [
 *     RouterModule.forRoot([
 *       {
 *         path: 'team/:id',
 *         component: TeamCmp,
 *         canDeactivate: [CanDeactivateTeam]
 *       }
 *     ])
 *   ],
 *   providers: [CanDeactivateTeam, UserToken, Permissions]
 * })
 * class AppModule {}
 * ```
 *
 * You can also provide a function with the same signature instead of the class:
 *
 * ```
 * @NgModule({
 *   imports: [
 *     RouterModule.forRoot([
 *       {
 *         path: 'team/:id',
 *         component: TeamCmp,
 *         canActivate: ['canDeactivateTeam']
 *       }
 *     ])
 *   ],
 *   providers: [
 *     {
 *       provide: 'canDeactivateTeam',
 *       useValue: (route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => true
 *     }
 *   ]
 * })
 * class AppModule {}
 * ```
 *
 * @stable
 */
export interface CanDeactivate<T> {
    canDeactivate(component: T, route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean;
}

But it's not clear what "TeamComponent" is.

like image 257
camden_kid Avatar asked May 23 '17 09:05

camden_kid


1 Answers

API documentation provides links to source code at the bottom of the page.

CanDeactivate interface uses generics to type the first canDeactivate argument (deactivated component):

export interface CanDeactivate<T> {
  canDeactivate(
      component: T, currentRoute: ActivatedRouteSnapshot, currentState: RouterStateSnapshot,
      nextState?: RouterStateSnapshot): Observable<boolean>|Promise<boolean>|boolean;
}

As shown in this guide, canDeactivate is called with deactivated component instance as an argument:

export interface CanComponentDeactivate {
 canDeactivate: () => Observable<boolean> | Promise<boolean> | boolean;
}

@Injectable()
export class CanDeactivateGuard implements CanDeactivate<CanComponentDeactivate> {
  canDeactivate(component: CanComponentDeactivate) {
    return component.canDeactivate ? component.canDeactivate() : true;
  }
}

Where component.canDeactivate is just a method of the same name in component class, it doesn't necessarily have to be called canDeactivate or even exist. And CanComponentDeactivate is just user-defined interface that provides a convention for canDeactivate component method.

This allows to interact with component during deactivation,

The canDeactivate() method provides you with the current instance of the component, the current ActivatedRoute, and RouterStateSnapshot in case you needed to access some external information. This would be useful if you only wanted to use this guard for this component and needed to get the component's properties or confirm whether the router should allow navigation away from it.

like image 191
Estus Flask Avatar answered Nov 11 '22 04:11

Estus Flask