Typescript泛型类参数

时间:2017-11-19 07:45:53

标签: typescript vue.js

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

我希望调用我的类,因为它会像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()
  }
}

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

2 个答案:

答案 0 :(得分:1)

您无法在Typescript中扩展泛型参数(在C#和Java中也是如此)。你可以做的是使用mixins方法。

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

我发现了两个缺点:

  1. 由于没有实现抽象方法而忽略了编译器警告
  2. 您将无法通过派生类访问静态方法。
  3. 执行魔法的功能

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

答案 1 :(得分:1)

我认为@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()
  }
}
祝你好运!