Typescript-实现泛型的类实例

时间:2020-02-27 19:08:10

标签: typescript generics

我有一个使用通用接口创建类的函数。实例的属性由该泛型的参数设置,如下所示:

const ClassFactory = <T>() => {

    class MyClass {
        constructor(data: T) {
            for (const key in data) {
                if (!data.hasOwnProperty(key)) { continue; }
                (this as any)[key] = data[key];
            }
        }

        // other methods can be here
    }

    return MyClass;
}

const GeneratedClass = ClassFactory<{ id: number }>();

const myInstance = new GeneratedClass({ id: 1 });

console.log((myInstance as any).id); // logs 1

这按预期运行,但是有两个问题

  1. myInstance的输入没有T键-我希望myInstance.id是一个数字
  2. 我必须在构造函数中强制转换this as any才能通过给定的数据分配值

为了解决第一个问题,我尝试了从其他帖子中看到的各种事情,包括 class MyClass implements T,但它们都导致相同的错误:A class can only implement an object type or intersection of object types with statically known members.ts(2422)。 我知道为什么会发生这种情况,但是由于定义类时知道T,有没有办法使它起作用?

如果我的数据位于public data: T属性中,则正确键入myInstance.data.id。所以我的问题是,可以通过跳过.data部分来做到这一点吗?

预先感谢

2 个答案:

答案 0 :(得分:4)

受到乔纳斯·威尔姆斯(Jonas Wilms)的评论的启发,即使该类具有方法/静态函数,我也可以通过返回以下内容使它起作用

return MyClass as (new (data: T) => T & InstanceType<typeof MyClass>) & typeof MyClass;

像这样,以下所有内容均按预期方式键入并运行

const myInstance = new GeneratedClass({ id: 1 });

console.log(myInstance.id, GeneratedClass.someStatic(), myInstance.someMethod());

但是,如果在类方法中使用new MyClass(),则无法正常工作。

一种可行的解决方法是创建一个静态方法,该方法返回具有正确类型的实例

        // inside the class
        public static build(data: T): T & InstanceType<typeof MyClass> {
            return new MyClass(data) as T & InstanceType<typeof MyClass>;
        }

那么以下是预期的

const myInstance = GeneratedClass.build({ id: 1 });

console.log(myInstance.id, GeneratedClass.someStatic(), myInstance.someMethod());

完整的示例

const ClassFactory = <T>() => {
    class MyClass {
        constructor(data: T) {
            for (const key in data) {
                if (!data.hasOwnProperty(key)) { continue; }
                (this as any)[key] = data[key];
            }
        }

        public static build(data: T): T & InstanceType<typeof MyClass> {
            return new MyClass(data) as T & InstanceType<typeof MyClass>;
        }

        public static someStatic() {
            return 2;
        }

        public someMethod() {
            return 3;
        }
    }

    return MyClass as (new (data: T) => T & InstanceType<typeof MyClass>) & typeof MyClass;
}

const GeneratedClass = ClassFactory<{ id: number }>();

const myInstance = new GeneratedClass({ id: 1 });

console.log(myInstance.id, GeneratedClass.someStatic(), myInstance.someMethod());

const myBuiltInstance = GeneratedClass.build({ id: 1 });

console.log(myBuiltInstance.id, GeneratedClass.someStatic(), myBuiltInstance.someMethod());

答案 1 :(得分:0)

我只是遇到了这个问题,并且能够通过简单地抛弃new并创建一个通用的静态方法Class.create()来解决问题,并将其实例化并转换为正确的类型。

interface MyGenericInterface<T = any> {
  getTheThing(): T
}

class MyClass implements MyGenericInterface {
  // Make constructor private to enforce contract
  private constructor(private thing: any) {}

  public getTheThing(): any {
    return this.thing
  }

  static create<TypeOfThing = any>(thing: TypeOfThing) {
    return new MyClass(thing) as MyGenericInterface<TypeOfThing>
  }
}

const stringyInstanceWrong = new MyClass('thing') // Error

const stringyInstance = MyClass.create('thing')
const stringThing: string = stringyInstance.getTheThing()

const numberyInstance = MyClass.create(123)
const numberyThingWrong: string = numberyInstance.getTheThing() // Error
const numberyThing: number = numberyInstance.getTheThing() // Works!