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

How to provide a service that uses generics in Angular4?

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

@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
  }
}

可以指定实体类型 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>
  ]
})

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

Value of type typeof DetailResolver is not callable. Did you mean to include new?

我不太熟悉 angular4 DI 内部结构。谁能描述出什么问题了,这个问题有什么解决办法吗?我正在使用 angular 4.3.

当您使用注入器注册提供者时,DI 系统使用该令牌——或者可以被视为 key 的东西——来维护令牌提供者映射每次请求依赖项时都会引用。

令牌通常是唯一的和符号化的对象,因此泛型的概念在这方面直接冲突正是因为令牌将作为密钥发挥作用。泛型是设计时的产物,换言之,它们将从生成的 JavaScript 中消失,因此不会留下它们的类型信息供 Angular 在运行时查找。出于同样的原因,TypeScript 接口也不是有效的标记。

摘要类

我的第一个选择是使用 abstract classes。摘要类这里解决两个问题: 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> {...}

然后在 NgModule 中,您将按以下方式提供:

providers: [{ provide: DetailResolver, useClass: MessageResolver }]

InjectionToken

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

const MESSAGE_RESOLVER = new InjectionToken<DetailResolver<Message, MessageService>>('MESSAGE_RESOLVER');

您会注意到,您可以在创建 InjectionToken 实例时提供类型,以使编译器一路支持您。当您在 NgModule.

中添加提供程序时,会强制执行此操作
providers: [{ provide: MESSAGE_RESOLVER, useClass: MessageResolver }]

值得注意的是 useClass 在技术上不是必需的; useValueuseFactory 等任何其他选项在提供相应内容时均在此处有效。

希望这对您有所帮助,并就此事提供一些澄清!