具有泛型参数的类装饰器

时间:2018-04-17 00:24:06

标签: typescript generics constructor decorator

我想写一个带有参数的类装饰器。麻烦的是我想强制执行与类本身相关的参数类型。

class Options<T> {
  accept: (instance: T)=>boolean;
}

var MyOptions: Options<MyClass>={
  accept: function (instance: MyClass)
            { return true; }
};

@deco(MyOptions)
class MyClass {
  y: string;
  constructor() {
    this.y="Hello";
  }
}

另一个目标是我需要对构造函数进行子类化,并赋予它对选项的访问权限,因此整个实现必须写在deco内。这是我通过试验和充分的错误得到的:

function imported<T>(instance: T, options: Options<T>): any { return {}; }

function deco<T extends {}>(options: Options<T>) {
  return function<CtorOfT extends {new(...args:any[]):T}>(ctr: CtorOfT) {
    let options_copy: Options<sub>=options;

    //replace constructor to add $accepted and $imported
    class sub extends ctr {
      $accepted: boolean;
      $imported: any;
      constructor(...args:any[]) {
        super(args);
        this.$accepted = options_copy.accept(this);
        this.$imported = imported<sub>(this, options_copy);
      }
    };
    return sub;
  }
}

编译器抱怨行

class sub extends ctr {

Base constructor return type 'T' is not a class or interface type.

我尝试用<T extends {}>解决这个问题,但这并没有太大帮助。它在我替换

时编译
new(...args:any[]):T

new(...args:any[]):{}

这就是我正在做的事情,但它失去了MyOptionsMyClass之间的类型联系。你可以很容易地猜到,我不知道我在做什么(错误)。

链接到使用TSC 2.8.1(Repl

测试的previous version

修改:按类型连接,我的意思是以下内容应该是错误:

class Foo {
  x: number;
}

var FooOptions: Options<Foo>={
  accept: function (instance: Foo)
            { return true; }
};

@deco(FooOptions) //error, Foo vs MyClass
class MyClass {
  ...

感谢jcalz,我注意到我将Options.accept声明为通用,但我不应该这样做。编辑出来,但现在抱怨

let options_copy: Options<sub>=options;

也是。我会尝试理解Github #16390 issue的讨论,但这确实令人费解。

1 个答案:

答案 0 :(得分:2)

我想现在还没有办法让mixin扩展泛型类型的构造函数,如Microsoft/Typescript#16390中所提到的那样。如果您认为这很重要,您可能想对该问题发表评论或给它一个或者什么。

正如我在评论中提到的那样,你可以断言你摆脱这个问题...你在deco()的实现中失去了一些类型的安全性,但至少从外部(调用者的#)事情应该有效。这是一种方法:

// name for "Constructor of"
interface Ctor<T> {
  new(...args: any[]): T
}

function deco<T>(options: Options<T>) {
  // give a name to the extended T we will return
  type XT = T & {
    $accepted: boolean;
    $imported: any;
  }
  // Use Ctor<T> below
  return function <CtorOfT extends Ctor<T>>(ctr: CtorOfT) {

    // leave options_copy as Options<T>
    let options_copy: Options<T> = options;

    // first assertion: ctr as Ctor<any>
    const sub = class extends (ctr as Ctor<any>) {
      $accepted: boolean;
      $imported: any;
      constructor(...args: any[]) {
        super(args);
        // second assestion: this as XT
        const that = this as any as XT;

        // use that instead of this
        this.$accepted = options_copy.accept(that);
        this.$imported = imported(that, options_copy);
      }

    // final assertion: sub as Ctor<XT>
    } as Ctor<XT>;
    return sub;
  }
}

在上文中,deco()需要Options<T>并返回一个Ctor<T>并返回Ctor<XT>的函数,其中XTT以及您添加到sub的扩展属性。因此,当您致电deco()时,您将获得所需的类型安全保障。

好的,那是我能找到你想要的最接近的。希望能帮助到你;祝好运。