【问题标题】:Typescript generic class parameters打字稿泛型类参数
【发布时间】:2018-05-02 15:37:24
【问题描述】:

我目前正在为项目编写一些内部抽象类,我需要它是通用的(我希望它是可扩展的)。

我希望调用我的类,因为它会扩展像 Sample extends T 这样的 T 模板,以便拥有 T 的所有参数。 例如,如果 T 是 Vue,我将拥有所有 Vue 参数,例如 $el$options,而无需重新声明 Vue 也不包含它。

所以我有以下内容:

export namespace SDK {

  export abstract class Sample<T> {

    private static methods: any = {
      hello: Sample.prototype.hello
    }

    abstract callMe () : void;

    x: number = 0
    y: number = 0
    width: number = 1
    height: number = 1

    hello (): void {
      console.log('Sample hello world')
      this.callMe()
    }
  }
}

但我不知道如何处理将 T 的属性包含到 Sample 中。

我希望它是这样的:

export namespace SDK {

  export abstract class Sample<T> {

    private static methods: any = {
      hello: Sample.prototype.hello
    }

    abstract callMe () : void;

    x: number = 0
    y: number = 0
    width: number = 1
    height: number = 1

    // T properties (Vue example)
    $el: HTMLElement
    ...

    hello (): void {
      console.log('Sample hello world')
      this.callMe()
    }
  }
}

我希望我的班级被称为:

export default class MyComponent extends SDK.Sample<Vue> {
  name: string = 'my-component';

  callMe () : void {
    console.log('called')
  }

  mounted () : void {
    this.hello()
  }
}

我没有找到任何关于从允许在其中包含参数的模板类扩展的信息。

【问题讨论】:

  • 澄清一下:你想扩展 Sample 还是 Sample 扩展 T?他们是不同的
  • 我想要 Sample 扩展 T.

标签: typescript vue.js


【解决方案1】:

您不能在 Typescript 中扩展泛型参数(在 C# 和 Java 中也一样)。您可以做的是使用mixins 方法。

根据您的以下要求是我想出的。我实际上并没有使用 Vue 进行测试(我没有使用该库设置环境),但是方法和属性是按预期从两个类继承的,因此它应该可以与 Vue 一起使用(如果您对任何问题发表评论,我可以看看进入他们)。

我发现了两个缺点:

  1. 您因未实现抽象方法而失去编译器警告
  2. 您将无法通过派生类访问静态方法。

施展魔法的函数

function extendSample<T, R extends { new(): T & Sample }>(componentCtor: new () => T): R {
    // Create a new derived class from the component class
    class DerivedComponent extends (<new () => any>componentCtor) {
        constructor() {
            // Call thec omponent constructor
            super();
            // Call out sample class constructor
            Sample.apply(this)
        }
    }
    // Copy instance methods to DerivedComponent
    Object.getOwnPropertyNames(Sample.prototype).forEach(name => {
        DerivedComponent.prototype[name] = Sample.prototype[name];
    });
    return <R><any>DerivedComponent;
}

示例代码:

export abstract class Sample {
    abstract callMe(): void;
    x: number = 0
    y: number = 0
    width: number = 1
    height: number = 1

    hello(): void {
        console.log('Sample hello world')
        this.callMe()
    }   
}

export class LibaryComponentBase {
    constructor() {
        this.$el = "HTML "
    }
    $el: string;
    public libraryMethod() {
        console.log("libraryMethod");
    }
    static Test() {

    }
}

export default class MyComponent extends extendSample(LibaryComponentBase) {
    name: string = 'my-component';
    constructor() {
        super();
        console.log(this.$el);
        this.libraryMethod();
        this.hello();
    }

    callMe(): void {
        console.log('called')
    }

    mounted(): void {
        this.hello();
    }
}

let my = new MyComponent();
my.callMe();

【讨论】:

  • 不确定为什么要将方法从构造函数原型复制到 mixin 原型。 mixin extends 构造函数不会自动实现这一点吗?
  • @jcalz 构造函数将是库基类(例如 vue),mixin 是示例类。所以DerivedComponent通过继承从库基类中获取方法,Sample通过复制获取方法
  • 哦,对不起,我现在明白了。
  • 感谢您的回答。 @jcalz 方法比我现在预期的要多,但你的观点将来可能对我有用。
【解决方案2】:

我认为@TitianCernicovaDragomir 基本上可以使用 mixins。为了完整起见,我将发布类似的代码,因为我认为我们采用的方法略有不同,它们的优缺点也有所不同。

以下代码确实强制您实现abstract 方法并允许您访问静态成员。但是您为此付出了代价,因为您使用了私有的未导出名称,这最终会阻止您将此库设为供他人使用的库。我认为有一些解决方法,但我不想在这个兔子洞里走得太远。

不管怎样,这里是Sample

export namespace SDK {

  export type Constructor<T> = {
    new(...args: any[]): T;
    readonly prototype: T;
  }

  export function Sample<C extends Constructor<{}>>(ctor: C) {
    abstract class Sample extends ctor {
      private static methods: any = {
        hello: Sample.prototype.hello
      }
      abstract callMe(): void;
      x: number = 0
      y: number = 0
      width: number = 1
      height: number = 1

      hello(): void {
        console.log('Sample hello world')
        this.callMe()
      }

    }
    return Sample;
  }

}

及其用法:

export default class MyComponent extends SDK.Sample(Vue) {
  name: string = 'my-component';

  callMe () : void {
    console.log('called')
  }

  mounted () : void {
    this.hello()
  }
}

祝你好运!

【讨论】:

  • 绝对是我需要的。感谢您的解释!
猜你喜欢
  • 1970-01-01
  • 2020-12-22
  • 2022-11-11
  • 2021-11-26
  • 1970-01-01
  • 1970-01-01
  • 2019-07-05
  • 2020-03-22
  • 1970-01-01
相关资源
最近更新 更多