Typescript:类继承泛型类型

14

我知道这个问题比较泛化,但是我希望创建一个类,该类具有来自类似于以下通用类型的所有属性和原型:

class GenericExtend<T> extends T {
    constructor(data: T) {
        // here is a workaround to make these 2 classes unique
        const proto = { ...GenericExtend.prototype };
        Object.assign(proto, Object.getPrototypeOf(data));
        Object.setPrototypeOf(this, proto);
        Object.assign(this, data);
    }

    GenericMethod() { }
}

从现在开始,我可以实例化GenericExtend类并像这样获取两个类的类型:

const obj = new GenericExtend<Model>(data);
obj.GenericMethod(); // ok
obj.ModelMethod(); // good!

我的解决方案之一是使用交集,类似于这样:

使用交集的方法之一是这样的:

const obj: GenericExtend & Model = new GenericExtend(data) as any;

它能够工作,但我并不是很喜欢。有更好的选择吗?
2个回答

15
TypeScript不允许您在编译时不知道所有T的键的情况下实现或扩展另一个类型T。这就防止了您编写类似于“class GenericExtend implements T {...}”的代码。
相反,您必须使用交集来获得此行为...但是如果您希望如此,可以将类型断言限制为构造函数,以便后续使用不需要它。让我们先将“GenericExtend”重命名:
class _GenericExtend<T> {
  constructor(data: T) {
    const proto = { ..._GenericExtend.prototype };
    Object.assign(proto, Object.getPrototypeOf(data));
    Object.setPrototypeOf(this, proto);
    Object.assign(this, data);
  }
  GenericMethod() { }
}

然后重新定义GenericExtend为一个类型和一个构造函数,具有你想要的交集行为:

type GenericExtend<T> = _GenericExtend<T> & T;
const GenericExtend: new <T>(data: T) => GenericExtend<T> = _GenericExtend as any;

最后的as any是我们需要的类型断言。现在,您应该能够获得所需的行为:

interface Model {
  ModelMethod(): void;
}
declare const data: Model;

const obj = new GenericExtend(data);
obj.GenericMethod(); // ok
obj.ModelMethod(); // ok
代码的Playground链接

太好了!如果Model是一个类而不是接口,它会起作用吗? - Eduardo Rosostolato
1
它在语法上应该是可行的,并且生成的对象在结构上应该与类类型匹配,但它可能无法使用instanceof或类原型属性。这些东西需要在GenericExtend构造函数的实现中解决...我不相信自己能写出来。如果您的用例是扩展类,则听起来您可能正在寻找mixins - jcalz
你说得对!我想mixin将会解决我的问题。 - Eduardo Rosostolato
1
感谢 @jcalz 提供的解决方案。我进一步简化了它,避免了额外的类型和对任何类型的转换:const GenericExtend = _GenericExtend as new <T>(data: T) => _GenericExtend<T> & T; - 4-bit

2
我曾遇到类似的需求,最终通过以下方式使其正常工作,不需要严格的交叉(您可以定义专用类型进行类型检查),希望能对您有所帮助。
class A {
  a() {
    ...
  }
}

class B {
  b() {
    ...
  }
}

type Constructor = new (...args: any[]) => any

function genericExtend<T extends Constructor>(target: T) {
  return class GenericExtended extends target {
    constructor(...args: any[]) {
      super(...args)
    }

    genericMethod() {
      ...
    }
  }
}

const instanceOfA: GenericExtended & A = new (genericExtend(A))()
const instanceOfB = new (genericExtend(B))()

instanceOfA.a() // ok with type checking
instanceOfA.genericMethod() // ok with type checking

instanceOfB.b() // ok without type checking
instanceOfB.genericMethod() // ok without type checking

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