Можно ли обеспечить безопасность типов для параметров декоратора метода? - PullRequest
2 голосов
/ 01 июня 2019

Я решил написать некоторые служебные декораторы, такие как 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;
  }
}

1 Ответ

1 голос
/ 01 июня 2019

Это известная проблема в TypeScript без очевидного решения (кроме ручного указания параметров универсального типа).

Проблема с реализацией этого, как объяснено в этом комментарии @ DanielRosenwasser , заключается в том, что использование декораторов похоже на вызов функции curry , и тип общего вывода, который вы хотите, был бы быть таким:

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 и дать ему ? или описать свой вариант использования, если вы считаете, что он более убедителен, чем другие упоминается. Удачи!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...