【问题标题】:Adding properties to a class via decorators in TypeScript通过 TypeScript 中的装饰器向类添加属性
【发布时间】:2019-07-15 17:39:46
【问题描述】:

在 TypeScript 的装饰器参考页面上,有一段代码片段说明了如何使用类装饰器覆盖构造函数:

function classDecorator<T extends {new(...args:any[]):{}}>(constructor:T) {
    return class extends constructor {
        newProperty = "new property";
        hello = "override";
    }
}

@classDecorator
class Greeter {
    property = "property";
    hello: string;
    constructor(m: string) {
        this.hello = m;
    }
}

console.log(new Greeter("world"));

在日志中:

class_1 {
  property: 'property',
  hello: 'override',
  newProperty: 'new property' }

到目前为止一切顺利。但是尝试通过点符号访问newProperty 失败:

类型'Greeter'.ts(2339) 上不存在属性'newProperty'

错误,它没有在 VS Code 的提示中列出。可以通过括号表示法访问它,但 TS 警告说

元素隐式具有“任何”类型,因为类型“Greeter”没有索引 signature.ts(7017)

我错过了什么吗?如何以类型安全的方式通过装饰器添加新属性?我希望像普通的类成员一样拥有普通的编译器支持。

【问题讨论】:

    标签: typescript decorator mixins


    【解决方案1】:

    不是直接的解决方案,而是避免装饰器限制的解决方法: 在某些情况下,可以将装饰器替换为可以正确继承类型的普通类。

    class ClassDecorator {
            newProperty = "new property";
            hello = "override";
    }
    
    class Greeter extends ClassDecorator {
        property = "property";
        hello: string;
        constructor(m: string) {
            this.hello = m;
        }
    }
    
    console.log(new Greeter("world"));
    
    

    【讨论】:

      【解决方案2】:
      function classDecorator<T extends { new(...args: any[]): {} }>(constructor: T) {
          return class extends constructor {
              newProperty = "new property";
              hello = "override";
          }
      }
      interface classInterface {
          newProperty: string;
          hello: string;
      }
      
      //trick
      interface Greeter extends classInterface { };
      
      @classDecorator
      class Greeter {
          property = "property";
          hello: string;
          constructor(m: string) {
              this.hello = m;
          }
      }
      const b = new Greeter();
      console.log(b.newProperty);
      

      看来我们可以使用接口技巧来解决问题。 技巧参考: https://stackoverflow.com/a/52373394/4831179

      【讨论】:

      • 这破坏了我们首先使用装饰器的原因。使用装饰器的目的是不必扩展类。如果没有,我们可以扩展为实现这些新属性的类,并在 Greter 的构造函数中调用 super()..
      • @JaviMarzán 这是目前唯一的选择,因为不允许类装饰器突变。见以下Github Issue:github.com/Microsoft/TypeScript/issues/4881 blackmiaool的技巧也可以在cmets中找到。
      【解决方案3】:

      设计的装饰器不能改变类的类型。这仍在讨论中,并且在装饰器提案最终确定之前,team 不会改变行为。你可以使用 mixins 来完成这个任务(阅读 mixins in ts

      使用 mixins,代码看起来像:

      function classDecorator<T extends { new(...args: any[]): {} }>(constructor: T) {
          return class extends constructor {
              newProperty = "new property";
              hello = "override";
          }
      }
      
      const Greeter = classDecorator(class {
          property = "property";
          hello: string;
          constructor(m: string) {
              this.hello = m;
          }
      });
      type Greeter = InstanceType<typeof Greeter> // have the instance type just as if we were to declare a class
      
      console.log(new Greeter("world").newProperty);
      

      【讨论】:

      • 为什么是“设计”?装饰器似乎是将 mixin 引入类的完美方式。
      • 我可以查找 GitHub 问题,它只是没有实施以允许这样做,是故意不这样做的决定。
      • @Forseti 这是一个未解决的问题,讨论很长,但这条评论似乎很相关:github.com/Microsoft/TypeScript/issues/…,所以更多的是尚未实施的问题
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-03-11
      • 2020-06-20
      • 2019-07-29
      • 2019-08-08
      • 2017-11-19
      • 1970-01-01
      • 2018-03-08
      相关资源
      最近更新 更多