TypeScript - 实现泛型类型的类实例

4

我有一个函数,使用通用接口创建一个类。实例的属性由该泛型的参数设置,就像这样:

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 强制转换为 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 部分来实现这一点呢?

提前感谢您的回复。


为什么要使用 javascript 标签呢? - Noob
@JonasWilms 感谢您的评论。通过使用 return MyClass as { new(data: T): T };,它可以正常地运行上面的示例,但是如果类中有任何方法,则会失败。理想情况下,它只应该覆盖 new,而不是整个类。 - Jason Athanasoglou
我认为如果你回答了自己的问题,应该将其作为答案发布,而不是作为问题的编辑。 - jcalz
3
@jcalz 是的,我刚做完了,感谢大家花时间和提供建议。 - Jason Athanasoglou
2个回答

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

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!

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接