【问题标题】:Tricky decorator class棘手的装饰类
【发布时间】:2019-02-22 13:44:20
【问题描述】:

我有以下代码 sn-p 我觉得很难理解:

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

【问题讨论】:

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


    【解决方案1】:

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

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

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

    在我看来,在这种情况下,泛型有点矫枉过正。它们对 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  { }
    

    【讨论】:

      【解决方案2】:

      类型约束

      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"
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-20
        • 2014-01-14
        • 1970-01-01
        • 2018-07-27
        • 2020-01-11
        相关资源
        最近更新 更多