【问题标题】:Typescript: Class extends a Generic Type打字稿:类扩展了通用类型
【发布时间】:2018-07-28 02:14:23
【问题描述】:

我知道它太通用了,但我希望创建一个类,该类将具有来自通用类型的所有道具和原型,如下所示:

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;

它有效,但我不太喜欢。有没有更好的办法?

【问题讨论】:

    标签: typescript


    【解决方案1】:

    TypeScript 不允许您实现或扩展另一个类型 T,除非 T 的所有键在编译时都是静态已知的。这会阻止class GenericExtend&lt;T&gt; 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 link to code

    【讨论】:

    • 太棒了!如果 Model 是一个类而不是一个接口,它会起作用吗?
    • 它应该在语法上工作,并且结果对象应该在结构上与类类型匹配,但它可能不适用于instanceof 或类原型属性之类的东西。这些东西需要在GenericExtend 构造函数的实现中解决……我不相信自己会写那个。如果您的用例是扩展一个类,听起来您可能正在寻找mixins
    • 你是对的!我猜 mixins 会解决我的问题。
    • 感谢@jcalz 这个解决方案。我进一步简化它以避免额外的类型和强制转换为任何类型:const GenericExtend = _GenericExtend as new &lt;T&gt;(data: T) =&gt; _GenericExtend&lt;T&gt; &amp; T;
    【解决方案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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-08-18
      • 1970-01-01
      • 2017-04-27
      • 1970-01-01
      • 2021-04-30
      • 1970-01-01
      相关资源
      最近更新 更多