Есть ли способ ввести декоратор метода TypeScript, чтобы ограничить тип метода, который он может декорировать? - PullRequest
0 голосов
/ 30 января 2020

Я хочу написать декоратор метода TypeScript, который можно применять только к методам с определенным типом первого аргумента. Это общая схема в кодовой базе, над которой я работаю, для передачи контекста запроса, имеющего дескрипторы для базы данных, метрик, ведения журнала и т. Д. c. Я хотел бы написать декоратор, который требует один из этих ресурсов в контексте запроса, но в остальном не зависит c от формы контекста запроса.

Вот стилизованный пример:

interface MyResource {
    logMetricsEtc(...args: any): void;
}

interface HasResourceINeed {
    myResource: MyResource;
}

function myDecorator<TFn extends ((tContext: HasResourceINeed, ...rest: any) => any)>(
    _target: object,
    key: string | symbol,
    descriptor: TypedPropertyDescriptor<TFn>,
): TypedPropertyDescriptor<TFn> | void {
    const originalHandler = descriptor.value!;

    descriptor.value = function (this: any, context: HasResourceINeed, ...inputs: any) {
        context.myResource.logMetricsEtc(...inputs);

        return originalHandler.apply(this, [context, ...inputs]);
    } as TFn;
}

При использовании с включенным strictFunctionTypes этот декоратор вызывает ошибку компиляции при применении к методу, который в противном случае выглядит разумным:

interface ServiceContext {
    myResource: MyResource;
    otherResource: {
        sendMessageEtc(msg: string): Promise<void>;
    };
}

class MyBusinessClass {
    // This causes a compile error, but shouldn't - the decorator will 
    // work here at runtime.
    @myDecorator
    async foo(context: ServiceContext, x: number): Promise<void> {

    }

    // This example MUST cause a compile error to prevent invalid 
    // usage - there's no resource available in the first arg for the
    // decorator to use.
    @myDecorator
    async bar(y: string): Promise<void> {

    }
}

Нежелательная ошибка компиляции выглядит следующим образом:

Argument of type 'TypedPropertyDescriptor<(context: ServiceContext, x: number) => Promise<void>>' is not assignable to parameter of type 'TypedPropertyDescriptor<(tContext: HasResourceINeed, ...rest: any) => any>'.
  Types of property 'value' are incompatible.
    Type '((context: ServiceContext, x: number) => Promise<void>) | undefined' is not assignable to type '((tContext: HasResourceINeed, ...rest: any) => any) | undefined'.
      Type '(context: ServiceContext, x: number) => Promise<void>' is not assignable to type '(tContext: HasResourceINeed, ...rest: any) => any'.(2345)

Я не могу разумно выключить strictFunctionTypes. Есть ли способ написать тип декоратора, чтобы принять foo, но отклонить bar?

1 Ответ

2 голосов
/ 30 января 2020

Предположительно, вы хотите, чтобы входные данные myDecorator() были обобщенными c в типе R первого аргумента оформленного метода и необязательно в типе Fn всего метода. Это позволит вам принимать методы, первый параметр которых является некоторым подтипом R вместо методов, которые являются подтипами Fn (что подразумевает, что их аргумент должен быть супертипами из R параметром метода это не то ограничение, которое вы хотите применить) ; удачи!

Детская площадка ссылка на код

...