棘手的装饰类

问题描述 投票:1回答:2

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

  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会保留现有属性还有额外添加的功能?

另外你能解释函数返回类型吗?我们是否在T中添加了额外的构造函数?

(class extends ctr {
            constructor(...args: any[]) {
                const [data] = args;
                if (data instanceof ctr) {
                    return data;
                }
                super(...args);
                (this as any)._completeInitialization();
            }
        });
typescript typescript2.0 typescript-generics typescript3.0
2个回答
1
投票

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

如果直接调用T将是该类。用于键入此装饰器的方法基本上是mixins(针对typescript here描述)的方法。

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

在我看来,仿制药在这种情况下有点过分。它们对mix​​in非常有用,因为它们将原始类从输入参数转发到输出参数(并且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
投票

Type Constraint

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将假定返回的对象没有任何属性。

Anonymous Class in Return

至于返回类型:由于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"
© www.soinside.com 2019 - 2024. All rights reserved.