如何获取TypeScript泛型匿名类型的字符串名称?Angular场景求助
解决Angular泛型服务获取类型名称的问题
你遇到的核心问题是TypeScript泛型是编译时特性,运行时会被擦除,所以T.name根本无法拿到实际的类型名称——这也是为什么你尝试直接访问它会无效的原因。另外,直接在构造函数加字符串参数导致Angular DI报错,是因为Angular会把构造函数参数默认当成依赖项去解析,而String类型没有默认的提供者。
下面给你几个可行的解决方案,按可靠性和灵活性排序:
方案1:通过实体构造函数+静态路由名称(最可靠,抗混淆)
这个方法完全不受代码混淆影响,因为我们手动指定了路由名称:
步骤1:修改泛型服务
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; @Injectable() export class CommonMasterdataProvider<T> { private readonly route: string; constructor( private http: HttpClient, // 传入实体的构造函数,要求实体类有静态的routeName属性 private entityCtor: { new (): T; routeName: string } ) { this.route = `/api/${entityCtor.routeName}`; } // 示例方法:获取列表 getList() { return this.http.get<T[]>(this.route); } }
步骤2:给你的实体类添加静态路由名称
export class User { id: number; name: string; // 手动指定路由名称,混淆时不会被修改 static readonly routeName = 'users'; }
步骤3:在组件/模块中提供服务
通过工厂函数告诉Angular如何创建服务实例:
import { Component } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { CommonMasterdataProvider } from './common-masterdata.provider'; import { User } from './user.model'; @Component({ selector: 'app-user', templateUrl: './user.component.html', providers: [ { provide: CommonMasterdataProvider, useFactory: (http: HttpClient) => new CommonMasterdataProvider<User>(http, User), deps: [HttpClient] // 声明依赖的服务 } ] }) export class UserComponent { constructor(private userProvider: CommonMasterdataProvider<User>) { // 使用服务 this.userProvider.getList().subscribe(users => console.log(users)); } }
方案2:通过实体构造函数获取类名(简单但依赖类名)
如果你的代码不会被混淆,或者混淆工具保留类名,可以直接用构造函数的name属性:
修改服务
@Injectable() export class CommonMasterdataProvider<T> { private readonly route: string; constructor( private http: HttpClient, private entityCtor: new () => T ) { // 直接拿类名当路由后缀 this.route = `/api/${entityCtor.name}`; } }
提供服务的方式和方案1一致
注意:如果开启了代码混淆(比如Angular生产构建的默认混淆),类名会被改成a、b这类短名称,导致路由错误,所以这个方案只适合开发环境或不混淆类名的场景。
方案3:使用InjectionToken传递路由名称(最灵活)
如果不想给实体类加静态属性,也可以用Angular的InjectionToken来传递路由名称:
步骤1:定义InjectionToken
import { InjectionToken } from '@angular/core'; export const ENTITY_ROUTE_SUFFIX = new InjectionToken<string>('ENTITY_ROUTE_SUFFIX');
步骤2:修改服务
import { Injectable, Inject } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { ENTITY_ROUTE_SUFFIX } from './route-suffix.token'; @Injectable() export class CommonMasterdataProvider<T> { private readonly route: string; constructor( private http: HttpClient, @Inject(ENTITY_ROUTE_SUFFIX) private routeSuffix: string ) { this.route = `/api/${routeSuffix}`; } }
步骤3:在组件/模块中提供服务和Token
@Component({ selector: 'app-product', templateUrl: './product.component.html', providers: [ { provide: ENTITY_ROUTE_SUFFIX, useValue: 'products' }, CommonMasterdataProvider ] }) export class ProductComponent { constructor(private productProvider: CommonMasterdataProvider<Product>) {} }
这个方案不需要修改实体类,完全通过DI传递参数,灵活性最高,也不受混淆影响。
为什么之前的字符串参数会报错?
Angular的依赖注入系统会默认把构造函数的所有参数当作依赖项,当你写constructor(private http: HttpClient, private routeSuffix: string)时,Angular会尝试找String类型的提供者,但默认不存在,所以抛出“无法找到String类型对应依赖”的异常。只有用@Inject()指定Token,或者通过工厂函数传递非依赖参数,才能避免这个问题。
内容的提问来源于stack exchange,提问作者Perrier
相关产品推荐
相关产品推荐

