简体   繁体   English

回调中的Typescript泛型

[英]Typescript generics in a callback

I'm trying to make a generic service that will get some remote data and create some objects with it. 我正在尝试提供一个通用服务,该服务将获取一些远程数据并使用它创建一些对象。

@Injectable()
export class tService<T> {
    private _data: BehaviorSubject<T[]> = new BehaviorSubject([]);
    private url = '...url...';  // URL to web api

    constructor(private http: HttpClient) {    
        this.http.get<T[]>(this.url).subscribe(theThings => {
            theThings = _.map(theThings, (theThing) => new T(theThing));
            this._data.next(theThings);
        });
    }
}

This gives me the error T only refers to type, but is being used as a value here . 这给了我错误T only refers to type, but is being used as a value here Ok, that's fine. 好没关系。 I understand what is happening. 我了解发生了什么事。 I've seen a couple of questions asking about something similar. 我看过几个类似问题的问题。

For Example: 例如:

It seems that any solution that I've come across either hardcodes in the class at some point, or, adds T's class constructor in somewhere. 看来,我遇到过的任何解决方案都是在某个时候遇到了类中的硬代码,或者在某个地方添加了T的类构造函数。 But I can't figure out how to do it. 但是我不知道该怎么做。 My problem is that the class being instantiated has parameters in the constructor and I'm using Angular's DI system, so I can't (???) add parmeters to the service's constructor. 我的问题是,要实例化的类在构造函数中具有参数,并且我使用的是Angular的DI系统,因此我不能(???)向服务的构造函数中添加参数。

constructor(private playersService: gService<Player>, private alliesService: gService<Ally>) { }

Can anyone figure out what I should do here? 谁能弄清楚我在这里应该做什么? Also, I'd prefer for an answer to not be some sort of 'hack', if possible. 另外,如果可能的话,我希望答案不是某种“ hack”。 If it a choice between doing something that is barely readable and just copy and pasting the service a couple of times and changing what class it is referring to, I'll take the second option. 如果要在做一些几乎不可读的事情,只是复制和粘贴服务几次,以及更改它所指的类之间做出选择,我将采用第二种选择。

A class name refers to both the of the class type but also to it's constructor, that is why you can write both let x: ClassName and new ClasName() . 类名既指类类型的,也指代其构造函数的,这就是为什么您可以同时编写let x: ClassNamenew ClasName() A generic parameter is only a type (much like an interface for example), that is why the compiler complains that you are using it as a value (the value being expected being a constructor function). 通用参数只是一种类型(例如,非常类似于接口),这就是为什么编译器会抱怨您将其用作值(期望该值是构造函数)的原因。 What you need to do is pass an extra parameter that will be the constructor for T : 您需要做的是传递一个额外的参数,该参数将成为T的构造函数:

export class tService<T> {
    private _data: BehaviorSubject<T[]> = new BehaviorSubject<T>([]);
    private url = '...url...';  // URL to web api

    constructor(private http: HttpClient, ctor: new (data: Partial<T>)=> T) {    
        this.http.get<T[]>(this.url).subscribe(theThings => {
            theThings = _.map(theThings, (theThing) => new ctor(theThing));
            this._data.next(theThings);
        });
    }
}
//Usage
class MyClass {
    constructor(p: Partial<MyClass>) {
        // ...
    }
}
new tService(http, MyClass)

Note The parameters to the constructor signature may vary according to your use case. 注意构造函数签名的参数可能会根据您的用例而有所不同。

Edit 编辑

You mention that you can't add arguments to the constructor, generics (and all types in general) are erased when we run the code, so you can't depend on T at runtime, somewhere, someone will have to pass in the class constructor. 您提到不能向构造函数添加参数,泛型(以及所有类型的普通类型)在我们运行代码时会被删除,因此您不能在运行时依赖T在某个地方,有人必须将其传递给类构造函数。 you can so this in several ways, the simplest version would be to create dedicated classes for each T instance, and then inject the specific class: 您可以通过几种方式做到这一点,最简单的版本是为每个T实例创建专用的类,然后注入特定的类:

class tServiceForMyClass extends tService<MyClass> {
    constructor(http: HttpClient)  {
        super(http, MyClass)
    }
} 

Or you could do your work dependent on the constructor, in an init method that requires the constructor as a parameter: 或者,您可以在需要构造函数作为参数的init方法中依赖构造函数进行工作:

export class tService<T> {
    private _data: BehaviorSubject<T[]> = new BehaviorSubject<T>();
    private url = '...url...';  // URL to web api

    constructor(private http: HttpClient){}
    init(ctor: new (data: Partial<T>)=> T) {    
        this.http.get<T[]>(this.url).subscribe(theThings => {
            theThings = _.map(theThings, (theThing) => new ctor(theThing));
            this._data.next(theThings);
        });
    }
} 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM