首页 > 解决方案 > 是否可以为方法装饰器选项提供类型安全

问题描述

我决定编写一些实用程序装饰器,例如memoize, rateLimiter。我想在没有不必要的样板代码的情况下实现尽可能多的类型安全。

是否可以在没有手动指定泛型的情况下确保装饰器中的完整类型安全?

type GET_FUNCTION_SIGNATURE<
  T extends TypedPropertyDescriptor<any>
> = T extends TypedPropertyDescriptor<infer U> ? U : never;

interface ITestDecoratorOptions<DECORATED_FUNCTION_ARGUMENTS_TYPE, DECORATED_FUNCTION_RETURN_TYPE> {
  getKeyFromArgs: (args: DECORATED_FUNCTION_ARGUMENTS_TYPE) => string;
  getDefaultValue: (args: DECORATED_FUNCTION_ARGUMENTS_TYPE) => DECORATED_FUNCTION_RETURN_TYPE;
}

const testDecorator = <TYPED_PROPERTY_DESCRIPTOR extends TypedPropertyDescriptor<any>>(
  options: ITestDecoratorOptions<
    Parameters<GET_FUNCTION_SIGNATURE<TYPED_PROPERTY_DESCRIPTOR>>,
    ReturnType<GET_FUNCTION_SIGNATURE<TYPED_PROPERTY_DESCRIPTOR>>
  >
) => {
  return (
    target: Object,
    key: string,
    descriptor = Object.getOwnPropertyDescriptor(target, key) as PropertyDescriptor
  ): TYPED_PROPERTY_DESCRIPTOR => {
    return null as any;
  };
};

class Test {
  //             \/ Is it possible to remove that generic and keep full type safety here?
  @testDecorator<TypedPropertyDescriptor<(a: number, b: string) => boolean>>({
    getKeyFromArgs: args => {
          // number               string
      return args[0].toString() + args[1]; // full type checking
    },
    getDefaultValue: args => {
      // full type checking: on args(number, string) and return type(boolean)
      if (args[0] === 1) {
        return true;
      }

      return false;
    }
  })
  public someMethod(a: number, b: string): boolean {
    return true;
  }
}

标签: typescripttypesdecoratortype-safety

解决方案


这是 TypeScript 中的一个已知问题,没有明显的解决方案(除了手动指定泛型类型参数)。

正如@DanielRosenwasser在此评论中所解释的那样,实现这一点的问题是使用装饰器就像调用咖喱函数一样,您想要的通用推理类型如下:

declare let f: <T>(callback: (x: T) => void) => (y: T) => void;
f(x => x.a)({ a: 100 }); // error!
//     ~ <-- T is inferred as {} or unknown, 

这不起作用,因为 TypeScript 在函数f的回调参数上调用函数时会推断泛型类型,并且不会等到返回的函数本身被调用。因此,当T类型实际上是编译器时,为时已晚,并且已经无法正确推断。

除了继续手动指定参数之外,我不知道我是否有任何建议,如果您认为它比提到的其他内容更引人注目,可能会在 TypeScript 中解决该问题并给出或描述您的用例。祝你好运!


推荐阅读