You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何获取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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 04:31:02