如何在Angular4中提供使用泛型的服务?

Upv*_*ote 8 angular-routing typescript-generics angular

我注意到我的项目有很多解析器服务,尽管使用不同的实体和存储库托管相同的代码.所以我接受了挑战,使用泛型将它们减少为单个解析器服务:

@Injectable()
export class DetailResolver<T, R extends Repository<T>> implements Resolve<T> {

  constructor(private repositoryService: R, private router: Router) {}

  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<T> {
    // use repositoryService to resolve this route
  }
}
Run Code Online (Sandbox Code Playgroud)

可以指定实体类型T和存储库类型R. 但是我在使用此服务时遇到问题:

const appRoutes: Routes = [
  // other routes
  {
    path: ':id',
    component: MessageEditComponent,
    resolve: {
      message: DetailResolver<Message, MessageService>
    }
  }
];

@NgModule({
  imports: [
    RouterModule.forChild(appRoutes)
  ],
  exports: [
    RouterModule
  ],
  providers: [
    DetailResolver<Message, MessageService>
  ]
})
Run Code Online (Sandbox Code Playgroud)

一旦我在DetailResolver<Message, MessageService>编译器中指定泛型类型,我就要创建一个实例:

类型typeof DetailResolver的值不可调用.你的意思是包括新的?

我对angular4 DI内部结构并不熟悉.任何人都可以描述什么是错的,这个问题有没有解决办法?我正在使用棱角4.3.

小智 10

当您使用注入器注册提供程序时,DI系统使用该令牌 - 或者可能被视为关键字 - 来维护每次请求依赖项时引用的令牌提供程序映射.

标记通常是唯一且具有符号性的对象,因此泛型的概念在该区域中直接冲突,因为标记将作为关键字起作用.泛型是一个设计时工件,这是另一种说法,它们将从生成的JavaScript中消失,因此不会留下他们的类型信息供Angular在运行时查找.出于同样的原因,TypeScript接口也不是有效的令牌.

抽象类

我的第一个选择是使用abstract.抽象类在这里解决了两个问题:1)虽然它们不能直接实例化,但与接口不同,它们可以包含实现细节,因此可以编译成Javascript.2)您通过基于扩展的架构获得DI令牌,这可能对您的架构非常有效.

在您的情况下,您可以执行以下操作:

@Injectable()
export abstract class DetailResolver<T, R extends Repository<T>> implements Resolve<T> {...}

@Injectable()
export class MessageResolver extends DetailResolver<Message, MessageService> {...}
Run Code Online (Sandbox Code Playgroud)

然后NgModule你将以下列方式提供它:

providers: [{ provide: DetailResolver, useClass: MessageResolver }]
Run Code Online (Sandbox Code Playgroud)

InjectionToken

另一种选择是使用InjectionToken(OpaqueToken在Angular 4.0之前称为s).InjectionTokens是专门用作DI令牌的对象; 然而,与他们的前身不同OpaqueToken,他们需要输入他们将注入的价值类型.

const MESSAGE_RESOLVER = new InjectionToken<DetailResolver<Message, MessageService>>('MESSAGE_RESOLVER');
Run Code Online (Sandbox Code Playgroud)

您会注意到,您可以在创建InjectionToken实例时提供类型,以便编译器在此过程中为您提供支持.当你在你的网站中添加提供程序时强制执行NgModule.

providers: [{ provide: MESSAGE_RESOLVER, useClass: MessageResolver }]
Run Code Online (Sandbox Code Playgroud)

值得注意的是,useClass那里技术上并不需要; 提供相应的任何其他替代方案,例如useValueuseFactory在此处有效.

希望这有用,并提供一些有关此事的澄清!