【问题标题】:TypeScript: type-safe class decorator that handles static methods?TypeScript:处理静态方法的类型安全的类装饰器?
【发布时间】:2018-04-11 01:11:56
【问题描述】:

如何编写一个类型安全的类装饰器来正确处理静态方法?

具体来说,这个装饰器适用于没有静态方法的类:

interface ITypeOf<T> {
    new(...args: any[]): T
}

function decorate<T>(cls: ITypeOf<T>): ITypeOf<T> {
    return cls
}

但是ITypeOf 不考虑静态属性,所以当它应用于具有静态属性的类时编译失败并出现此错误:

@decorate
class Foo {
    static bar() {
        return 42
    }
}

产生错误:

Unable to resolve signature of class decorator when called as an expression.
  Type 'ITypeOf<Foo>' is not assignable to type 'typeof Foo'.
    Property 'bar' is missing in type 'ITypeOf<Foo>'.
function decorate<T>(cls: ITypeOf<T>): ITypeOf<T>

这是一个工作示例: http://www.typescriptlang.org/play/#src=interface…

如何编写适用于具有静态成员的类的类型安全的类装饰器?

【问题讨论】:

    标签: typescript


    【解决方案1】:

    装饰器的返回类型必须与它正在装饰的类类型兼容,因为装饰器的返回值将在运行时替换类。为了实现这一点,我们必须使用一个表示类本身的泛型参数,它既是参数的类型,也是返回类型。因此,我们将返回一个与输入类(包括静态)具有相同结构的类,并且编译器将得到满足:

    function decorate<TCtor extends ITypeOf<any>>(cls: TCtor): TCtor {
        return cls
    }
    
    @decorate
    class Foo {
        static bar() {
            return 42
        }
    }
    

    要对要修饰的类添加限制,我们可以将类型参数限制为ITypeOf

    // decorated class must have a field x: numeber
    function decorate<TCtor extends ITypeOf<{ x: number }>>(cls: TCtor): TCtor {
        return cls
    }
    
    @decorate // error no instance x
    class Foo { }
    @decorate // ok
    class Boo { x!: number }
    

    我们还可以对静态成员添加限制

    // decorated class must have a static field x: numeber
    function decorate<TCtor extends ITypeOf<any> & { x: number }>(cls: TCtor): TCtor {
        return cls
    }
    
    @decorate // error no static x
    class Foo { }
    @decorate // ok
    class Boo { static x: number }
    

    【讨论】:

      【解决方案2】:

      如何编写适用于类的类型安全的类装饰器 使用静态成员?

      这取决于你要在装饰器中做什么。

      您可以像这样简单地将decorator 完全通用:

      function decorate<C>(cls: C) {
          return cls;
      }
      

      然而,这并没有捕获任何关于C 的信息,所以你不能在decorate 中使用cls 做很多事情。

      或者您可以有单独的通用参数,一个用于cls 的“实例端”,另一个用于“静态端”:

      interface ITypeOf<T> {
          new(...args: any[]): T
      }
      
      function decorate<I, S extends ITypeOf<I>>(cls: S): S {
          return cls
      }
      
      
      @decorate
      class Foo {
          static bar() {
              return 42
          }
      
      }
      

      【讨论】:

      • 我考虑了两个参数的方法,但我不确定它增加了什么价值。如果您需要装饰器中的实例类型,您可以使用InstanceType,并且为了限制装饰类,将参数限制为ITypeOf 也可以。我实际上尝试使用两个通用参数手动调用装饰器(即decorate(Foo)),即使强制执行类型约束,I 也会被推断为可能的最小类型(即{},如果没有应用约束),这样提示我决定删除它。
      • 问题中定义的 ITypeOf 非常普遍,更广为人知的是 type Constructor&lt;T&gt;=...introduced for example here。如果您有几个不同的装饰器,每个都需要自己的限制,则两个参数方法允许您就地声明限制和装饰器定义。限制ITypeOf 的参数将需要针对每种限制进行额外的接口定义。不过没有太大区别,我同意。
      猜你喜欢
      • 1970-01-01
      • 2018-06-30
      • 1970-01-01
      • 2021-01-24
      • 1970-01-01
      • 1970-01-01
      • 2021-08-17
      • 2020-12-09
      • 2016-05-19
      相关资源
      最近更新 更多