整decor的装饰班

时间:2018-09-18 13:42:16

标签: typescript typescript2.0 typescript-generics typescript3.0

我有以下代码片段,很难理解:

  export class Record{

  };

  export class RecordMissingExtendsError{
      constructor(r:any){

      }
  }


  export function Model() {
    return <T extends { new(...args: any[]): {} }>(ctr: T) => {
        if (!(ctr.prototype instanceof Record)) {
            throw new RecordMissingExtendsError(ctr);
        }

        return (class extends ctr {
            constructor(...args: any[]) {
                const [data] = args;
                if (data instanceof ctr) {
                    return data;
                }
                super(...args);
                (this as any)._completeInitialization();
            }
        });
    };
}

我很难理解上面的代码,并且理解如下:

模型返回类型T(我知道什么是泛型,所以不用担心解释泛型),其中

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

以上是什么意思?是否要保留现有属性以及额外添加的功能?

此外,您能否解释函数的返回类型?我们是否要在T中添加一个额外的构造函数?

(class extends ctr {
            constructor(...args: any[]) {
                const [data] = args;
                if (data instanceof ctr) {
                    return data;
                }
                super(...args);
                (this as any)._completeInitialization();
            }
        });

2 个答案:

答案 0 :(得分:1)

T extends { new(...args: any[]): {} }意味着T必须是构造函数(即类)。构造函数参数和返回类型无关紧要(T可以具有任意数量的参数,并且可以返回扩展{}的任何类型,实际上是任何对象类型)。

如果直接调用T将是该类。基本上,用于键入此装饰器的方法是mixin的方法(针对打字稿here进行了描述)。

该函数的返回值将是一个继承装饰后的类的 new 类。因此,它不是添加构造函数,而是用新的构造函数替换原始构造函数,并通过super调用来调用原始构造函数。

在我看来,在这种情况下,仿制药有点矫kill过正。它们对于mixin非常有用,因为它们将原始类从输入参数转发到输出参数(并且mixin将成员添加到类型中)。但是,由于装饰器无法更改类型的结构,因此没有任何可转发的内容。另外,与其构造函数返回{},而是键入它以返回Record,因为您在运行时检查了它,也可能在编译时检查了它:

export class Record{
    protected _completeInitialization(): void {}
};

export function Model() {
  return (ctr: new (...a: any[]) => Record ) => {
      if (!(ctr.prototype instanceof Record)) {
          throw new RecordMissingExtendsError(ctr);
      }

      return (class extends ctr {
          constructor(...args: any[]) {
              const [data] = args;
              if (data instanceof ctr) {
                  return data;
              }
              super(...args);
              this._completeInitialization(); // no assertion since constructor returns a record
          }
      });
  };
}

@Model()
class MyRecord extends Record { }

@Model()// compile time error, we don't extend Record
class MyRecord2  { }

答案 1 :(得分:1)

类型约束

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

在这里,类型T被限制为扩展{ new(...args: any[]): {} }的任何类型。此处的格式可能会有些混乱-格式正确,类型看起来像这样:

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

这描述了所谓的 newable ,这是需要使用new 调用的某种功能对象。例如:

let A: { new(): any; };
A(); // not ok
new A(); // ok

let B: { new(foo: string): any; };
B(); // not ok
new B(); // not ok, param missing
new B('bar'); // ok

...args: any[]仅仅是rest parameter declaration,返回类型声明{}意味着需要返回一个对象。 TypeScript将假定返回的对象不具有任何属性。

返回中的匿名类

关于返回类型:由于Model装饰器函数是类装饰器,因此它可以返回类本身。如果确实返回一个类,则将使用该类而不是修饰的类。

  

如果类装饰器返回一个值,它将用提供的构造函数替换类声明。

     

from the TS handbook

例如:

// `ctr` is a common abbreviation for "constructor"
function Decorate(ctr: Function) {
    return class {
        constructor() {
            super();
            console.log('decorated');
        }
    };
}

@Decorate
class A {
    constructor() {
        console.log('A');
    }
}

new A(); // this will log: "A" first, then "decorated"