棘手的装饰器 class

Tricky decorator class

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

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

我很难理解上面的代码并理解以下内容:

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

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

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

另外可以解释一下return函数的类型吗?我们是否要向 T 添加一个额外的构造函数?

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

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

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

函数的 return 值将是 new class 继承修饰的 class。因此,它不是添加构造函数,而是用新构造函数替换原始构造函数,并通过 super 调用调用原始构造函数。

在我看来,在这种情况下,泛型有点矫枉过正。它们对 mixins 很有用,因为它们将原始 class 从输入参数转发到输出参数(并且 mixin 将成员添加到类型)。但是由于装饰器不能改变类型的结构,所以没有什么可以转发的。此外,而不是构造函数 returning {} 我将其键入 return 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  { }

类型约束

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,return类型声明,{}表示一个对象需要returned。 TypeScript 将假定 returned 对象没有任何属性

匿名 Class 在 Return

至于return类型:由于Model装饰器函数是class装饰器,它可以return一个class本身。如果它 return a class,将使用 class 而不是修饰的 class.

If the class decorator returns a value, it will replace the class declaration with the provided constructor function.

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"