【问题标题】:Get name of type in generic Angular service获取通用 Angular 服务中的类型名称
【发布时间】:2018-12-16 10:00:40
【问题描述】:

我可以像这样得到一个对象的名称:

let name = obj.prototype.constructor.name;

但我想做的是在我的通用 Angular 服务中获取通用类型的名称。

@Injectable({
  providedIn: 'root'
})
export class MyService<T> {

    private _value: T;

    constructor(){}

    //... other methods

    private error(error: any)
    {
      console.log(`Error for service type: ${_value.prototype.constructor.name}`)
    }

}

_value.prototype.constructor.name 不起作用,类型 T 的原型不存在。

我已经看到 typescript 特定的帖子指出只有在构造函数采用该类型的参数时才可能这样做,因为一旦转译的类型声明被删除,因为它们在 javascript 中不存在。

由于 Angular 通过依赖注入创建此服务,传递类型的正确方法是什么,以便可以以允许我将名称读取为字符串的方式对其进行实例化?

我在这个帖子里看到过:How to get name of generic type T inside service in Angular

它说要使用这个功能:

function createInstance<T>(type: new() => T): T {
    return new type();
}

但我看不出我应该如何使用与我上面的角度服务相关的这个功能。

如何获取服务中的字符串?

【问题讨论】:

    标签: angular typescript


    【解决方案1】:

    您无法通过类型检查的方式进行这项工作。
    如果你可以使用typeof T,这将起作用。

    // doesn't work
    @Injectable({
      providedIn: 'root'
    })
    export class MyService<T> {
      private _value: T;
      private _type: typeof T = T;
                            ^^^^^ 'T' refers to a type but is being used as a value
      private error() {
        console.log(`Error for service type: ${this._type.prototype.constructor.name}`)
      }
    }
    

    您可以进行一些调整来完成这项工作。在继续之前,请意识到您将丢失一些类型检查。

    • typeof T 替换为Function(基本上是any 用于类)
    • 添加构造函数参数,用于传递实际类型
    • 删除 @Injectable() 装饰器...Angular 不会帮助您找到实际类型。

    重构后:

    export class MyService<T> {
      private _value: T;
      private _type: Function;
      constructor(type: Function) {
        this._type = type;
      }
      private error() {
        console.log(`Error for service type: ${this._type.prototype.constructor.name}`)
      }
    }
    

    基本用法如下:

    let myStringService = new MyService<String>(String);
    

    只是为了证明你丢失了类型检查,这将编译没有错误:

    class HeckinBamboozled { }
    let myNumberService = new MyService<Number>(HeckinBamboozled);
    

    现在您有两个选项可以使用 DI 进行这项工作。最简单的方法是创建和提供派生类型。

    @Injectable({ providedIn: 'root' })
    export class MyStringService extends MyService<String> {
      constructor() {
        super(String);
      }
    }
    

    这很容易被其他服务消耗。

    @Injectable()
    export class OtherService {
      constructor(private stringService: MyStringService) {
      }
    }
    

    第二个选项更漂亮,使用注入令牌。

    export const STRING_SERVICE = new InjectionToken<MyService<String>>('My String Service', {
      providedIn: 'root',
      factory: () => new MyService<String>(String)
    });
    

    但是比较难吃。

    @Injectable()
    export class OtherService {
      constructor(@Inject(STRING_SERVICE) private stringService: MyService<String>) {
      }
    }
    

    而且它实际上比第一个选项更不安全。这编译没有错误。

    @Injectable()
    export class OtherService {
      constructor(@Inject(STRING_SERVICE) private stringService: MyService<HeckinBamboozled>) {
      }
    }
    

    【讨论】:

      猜你喜欢
      • 2018-08-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-02-16
      • 1970-01-01
      • 2011-02-04
      • 1970-01-01
      相关资源
      最近更新 更多