【问题标题】:Multi purpose TypeScript decorator多用途 TypeScript 装饰器
【发布时间】:2020-06-16 16:44:35
【问题描述】:

我正在尝试定义一个可以在类和属性上使用的装饰器。代码有效,但我无法使类型定义有效。我目前有以下:

type Decorator<T> = T extends Function
  ? (target: T) => T
  : (target: T, propertyKey: string) => void;

function Schema<T>(): Decorator<T> {
  return (target: T, propertyKey?: string) => {
    if (target instanceof Function) {
      return target;
    }
  };
}

@Schema()
export default class MyClass {
  @Schema()
  bool: boolean;
}

Playground

如果我明确使用将通用 T 设置为 typeof BooleanProperty,它会起作用。但是,我希望 Typescript 能够推断出类型并停止抱怨。

我还尝试了其他选项,例如函数重载,但没有任何成功。

我该如何解决这个问题?

【问题讨论】:

    标签: typescript typescript-decorator


    【解决方案1】:

    您可能希望您的装饰器工厂是非泛型的,并让它生成一个泛型装饰器。现在你的装饰器工厂是通用的并且返回一个非通用的装饰器。这就是两者的区别:

    // bad
    declare const genericFactoryForSpecificFunction: <T>() => (x: T) => T;
    const oops = genericFactoryForSpecificFunction()(123); // unknown
    
    // good
    declare const specificFactoryForGenericFunction: () => <T>(x: T) => T;
    const okay = specificFactoryForGenericFunction()(123); // 123
    

    在不好的情况下,当您调用 genericFactoryForSpecificFunction() 时,编译器必须推断出 T,但在该表达式中没有 T 类型的值可供它参考。所以它选择unknown,这意味着对参数123 的后续调用返回unknown。在好的情况下,编译器会将T 的推断推迟到对specificFactoryForGenericFunction() 返回的函数的调用。这让编译器实际上使用T 类型的值来推断T,因此你得到123


    进行此更改后,您将需要您的 Decorator 类型是表示通用函数调用签名的特定类型。我想可以将您的条件类型转换为该形式,但它有点混乱并且使用了其余元组:

    type PossibleDecorator = <T>(
      target: T,
      ...args: T extends Function ? [] : [string?]
    ) => T extends Function ? T : void;
    
    declare const pd: PossibleDecorator;
    pd(RegExp); // okay, class decorator
    pd({a: ""}, "a") // okay, prop decorator
    pd(RegExp, "oops"); // error, tried to pass prop to class decorator
    

    我会说多重调用签名重载会更好,并且具有相同的行为:

    type Decorator = {
      <T extends new (...args: any) => any>(ctor: T): T; // class decorator
      <T, K extends keyof T>(proto: T, member: K): void; // prop decorator
    }
    
    declare const d: Decorator;
    d(RegExp); // okay, class decorator
    d({ a: "" }, "a") // okay, prop decorator
    d(RegExp, "oops"); // error, tried to pass prop to class decorator
    

    所以,你的示例代码会变成这样:

    function Schema(): Decorator {
      return (target: any, propertyKey?: string) => {
        if (target instanceof Function) {
          return target;
        }
        return;
      };
    }
    
    @Schema()
    export default class MyClass {
      @Schema()
      bool: boolean = false;
    }
    

    看起来不错。


    好的,希望对您有所帮助;祝你好运!

    Playground link to code

    【讨论】:

      猜你喜欢
      • 2017-06-23
      • 1970-01-01
      • 2018-06-08
      • 2019-07-29
      • 2022-08-15
      • 1970-01-01
      • 2016-12-25
      • 2019-02-23
      • 2017-11-15
      相关资源
      最近更新 更多