【问题标题】:How to type decorators in TS如何在 TS 中键入装饰器
【发布时间】:2021-04-12 09:25:55
【问题描述】:

我想知道如何在 Typescript 中正确输入 method decorators。我有一个简单的装饰器,它将字符串参数作为第一个参数添加到装饰函数中。它工作得很好,问题是我不知道如何正确输入它以及它是否可能?

const task = (
  target: unknown,
  propertyKey: string,
  descriptor: TypedPropertyDescriptor<(...args: unknown[]) => unknown>,
): TypedPropertyDescriptor<(name: string, ...args: unknown[]) => unknown> => {
  const original = descriptor.value
  descriptor.value = function (...args: unknown[]) {
    return original?.call(this, 'Mark', ...args)
  }

  // what's wrong with this return?
  return descriptor
}



class Test {

    constructor(){
      // should not report an error
      this.printName()
    }


    @task
    printName(name: string) {
        console.log(name)

    }

}


  new Test()

有游乐场here

【问题讨论】:

    标签: typescript


    【解决方案1】:

    正如@cdimitroulas 所说,这是不可能的,装饰器不会改变 Typescript 类型。

    不过,我们可以解决您的一些问题:

    // 这个返回有什么问题?

    const task = (
      target: unknown,
      propertyKey: string,
      // you said here that descriptor is this type
      descriptor: TypedPropertyDescriptor<(...args: unknown[]) => unknown>,
      // yet, you said that the type is different here, namely that the first argument is of type string (it was unknown)
    ): TypedPropertyDescriptor<(name: string, ...args: unknown[]) => unknown> => {
      const original = descriptor.value
      descriptor.value = function (...args: unknown[]) {
        return original?.call(this, 'Mark', ...args)
      }
    
      // you're returning the same descriptor that was passed to the decorator
      return descriptor
    }
    

    Typescript 期望找到相同的类型,但它是不同的,因此它会尝试比较它们以查看一个是否可以分配给另一个,但事实并非如此。 unknown 不能分配给 stringunknown 只能分配给 unknownany。 知道装饰器无论如何都不会改变类型,你可以完全省略返回类型,让 Typescript 为你推断

    const task = (
      target: unknown,
      propertyKey: string,
      descriptor: TypedPropertyDescriptor<(...args: unknown[]) => unknown>,
    ) => {
      const original = descriptor.value
      descriptor.value = function (...args: unknown[]) {
        return original?.call(this, 'Mark', ...args)
      }
    
      return descriptor
    }
    

    接下来,Typescript 仍然对此不满意:

    @task // --> Type '(name: string) => void' is not assignable to type '(...args: unknown[]) => unknown'
        printName(name: string) {
            console.log(name)
    
        }
    

    如前所述,unknown 不能分配给string。因此,要么将unknown 更改为any(不安全),要么将签名更改为TypedPropertyDescriptor&lt;(name: string) =&gt; void&gt;

    const task = (
      target: any,
      propertyKey: string,
      descriptor: TypedPropertyDescriptor<(name: string) => void>,
    ) => {
      const original = descriptor.value
      descriptor.value = function () {
        return original?.call(this, 'Mark')
      }
    
      return descriptor
    }
    

    至于这一行

    this.printName()
    

    您能做的最好的事情就是将 name 属性设为可选。装饰者基本上是在为我们设置道具,所以在这种情况下,我们可以完全移除道具,尽管我知道这不是一个真实的案例,只是一个例子来说明你的观点。

    【讨论】:

    • 很好的解释:)
    【解决方案2】:

    无法使用装饰器修改函数的类型签名。

    查看相关讨论herehere

    【讨论】:

      猜你喜欢
      • 2022-11-28
      • 2021-01-17
      • 2018-08-07
      • 2021-11-18
      • 1970-01-01
      • 2018-06-08
      • 2021-03-29
      • 2018-03-31
      • 2022-07-21
      相关资源
      最近更新 更多