如何从Class <T>`方法内部实例化T?

时间:2019-08-11 00:43:15

标签: typescript

仍在学习TypeScriptus。

我正在为Web应用程序创建模型和集合系统,我需要我的Collection类才能实例化任何Model子类在构造时传递的实例,例如:{{1 }}将创建new Collection<Post>()的列表;和Post将创建new Collection<Comment>()的列表。

正如您将在下面粘贴的代码中看到的那样,我可以按原样执行axample,并且不会出错,但是我想从内部实例化Comment any 子类Collection类不只是Model。我该如何实现?

Post

关键所在的是// A Model attributes form API interface Attr { id: number; title: string; } // Base model class abstract class Model { protected attr: Attr; public constructor(attr: Attr) { this.attr = attr; } } // An actual model to be used in the app class Post extends Model { public constructor(attr: Attr) { super(attr); // other custom stuff, like API adapters, etc. } } // A collection of Model using type T class Collection<T extends Model> { private collected: T[] = []; public async fetch(): Promise<Collection<T>> { const attrList: Attr[] = await new Promise<Attr[]>((resolve): void => { resolve([{ id: 1, title: 'Post 1' }, { id: 2, title: 'Post 2' }] as Attr[]); }); this.collected = attrList.map((attr: Attr): T => { // I can instantiate Post from here and cast it, // but I want to use whatever is passed in T instead return new Post(attr) as T; }) return this; } public all(): T[] { return this.collected; } } const collection = new Collection<Post>(); const posts: Post[] = collection.all(); Post的硬编码声明,例如:Collection.fetch()

如何实例化有效的return new Post(attr) as T;,例如,同时适用于TPost类?

2 个答案:

答案 0 :(得分:3)

您的Collection<T>类实例需要保留T的构造函数实例。

此外:对于接口使用名称Attr可能不是一个好主意,因为已经有a type in global scope named Attr可能是名称冲突或名称隐藏,这两种都不是特别令人愉快的事情处理。从这里开始,我将名称更改为MyAttr

构造Collection<T>时,需要传入T对象的构造函数,可以使用MyAttr参数进行调用,并且Collection<T>需要保持到该构造函数上以在fetch()方法实现中使用。在Typescript中,此类构造函数的类型为new (attr: MyAttr) => T。通过使用parameter property,我们可以简单地表示“传递并保持”操作:

class Collection<T extends Model> {
  private collected: T[] = [];
  public constructor(private ctor: new (attr: MyAttr) => T) {}

(如果我们想写出来,就像private ctor: new (attr: MyAttr) => T; public constructor(ctor: new (attr: MyAttr) => T) { this.ctor = ctor; }

然后在fetch()方法内,我们使用this.ctor

  public async fetch(): Promise<Collection<T>> {
    const attrList = await new Promise<MyAttr[]>(
      (resolve): void => {
        resolve([
          {
            id: 1,
            title: "Post 1"
          },
          {
            id: 2,
            title: "Post 2"
          }
        ]);
      }
    );
    this.collected = attrList.map(attr => new this.ctor(attr));
    return this;
  }

这应该对您有用。 (请注意,我删除了用于Promise解析的类型断言...使用内置的Attr接口修复名称冲突后就没有必要了)

最后,当您制作collection时,您传入了Post构造函数。您不再需要将通用T手动指定为Post,因为编译器会为您推断出来:

const collection = new Collection(Post);
const posts: Post[] = collection.all();

好的,希望能有所帮助。祝你好运!

Link to code

答案 1 :(得分:0)

据我所知,你做不到。

在我的应用程序上,我通过创建抽象方法getInstance()解决了(实际上是解决方法):

abstract public getInstance(... args): T

因此,在我的普通模型上,我实现了此方法:

public getInstance(...args): Post {
    return new Post(...args);
}