代码之家  ›  专栏  ›  技术社区  ›  Guerrilla

获取通用角度服务中类型的名称

  •  2
  • Guerrilla  · 技术社区  · 5 年前

    我可以这样得到一个物体的名字:

    let name = obj.prototype.constructor.name;
    

    但我要做的是在我的通用角度服务中获取通用类型的名称。

    @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();
    }
    

    但是我看不出,我应该如何使用这个函数来处理我上面的角服务。

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

    1 回复  |  直到 5 年前
        1
  •  2
  •   Steven Liekens    5 年前

    您不能以类型检查的方式使此工作。
    如果你能使用 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}`)
      }
    }
    

    你可以做一些调整来使这项工作生效。在继续之前,请意识到您将丢失一些类型检查。

    • 代替 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>) {
      }
    }