Перегрузки метода TypeScript - PullRequest
2 голосов
/ 14 июля 2020

У меня есть такой код, метод с двумя перегрузками:

/**
 * Returns all keys of object that have specific value:
 * @example
 * KeysOfType<{a:1, b:2, c:1}, 1> == 'a' | 'c'
 */
type KeysOfType<MapT extends Record<string, any>, ValT> = {
    [K in keyof MapT]: MapT[K] extends ValT ? K : never;
}[keyof MapT];

export class Hooks<EventsMap extends Record<string, any>> {
    fireHooks<K extends KeysOfType<EventsMap, void>>(
        event: K,
        context: void,
    ): Promise<void>;

    fireHooks<K extends keyof EventsMap>(
        event: K,
        context: EventsMap[K],
    ): Promise<void>;

    fireHooks<K extends keyof EventsMap>(event: K, context: EventsMap[K]) {
        // ...
    }
}

Предполагается, что он будет использоваться вот так:

type MyHooks = { 
  aaa: void; 
  bbb: { data: string } 
};
let h = new Hooks<MyHooks>();

h.fireHooks('aaa');
h.fireHooks('bbb', { data: 'data' });

Есть общий c класс Hooks с методом fireHooks, который использует сопоставление имени события и данных контекста для проверки контекста события. Некоторые события можно вызывать без какого-либо контекста, поэтому я хочу, чтобы этот метод вызывал эти события только с одним аргументом.

Все работает нормально, но проблема в том, что эти перегрузки выглядят некрасиво и излишне.

Если я пропускаю перегрузки, TS жалуется на h.fireHooks('aaa'); с

Expected 2 arguments, but got 1.ts(2554)

Если я пропускаю только вторую перегрузку, которая кажется 100% копией реализации, TS жалуется на h.fireHooks('bbb', { data: 'data' }); с

Argument of type '"bbb"' is not assignable to parameter of type '"aaa"'.ts(2345)

Может кто-нибудь объяснить

  • почему мне нужна 1-я перегрузка, чтобы можно было пропустить параметр
  • и почему мне нужна 2-я перегрузка, которая кажется избыточный, поскольку он полностью совпадает с реализацией?

1 Ответ

0 голосов
/ 14 июля 2020

«зачем мне вторая перегрузка, которая кажется избыточной, поскольку она полностью совпадает с реализацией?»

Когда вы используете перегрузки, вы не можете на самом деле вызывает реализацию напрямую.

Обратите внимание:

function foo(a: string, b: string): void;
function foo(a: number, b: number): void;
function foo(a: number | string, b: number | string): void {
  //...
}

foo(1, 2) // good
foo('a', 'b') // good
foo(1, 'b') // Error: No overload matches this call.

Хотя реализация foo позволяет одному аргументу быть числом, а другому - строкой, перегрузки не допускайте этого.

Так что у вас никогда не бывает только одной перегрузки и одной реализации. Они будут идентичны.

У вас либо одна функция с одной сигнатурой, либо у вас есть две или более перегрузки, а также одна реализация.

«зачем мне нужна первая перегрузка, чтобы можно было пропустить параметр»

// You can also leave off the argument entirely
fireHooks<K extends KeysOfType<EventsMap, void>>(
    event: K,
): Promise<void>;

Потому что у вас есть две сигнатуры функций, и вам нужна перегрузка для каждого, поскольку (как указано выше) вы не можете напрямую вызвать реализацию. У каждого свой тип event, поскольку они работают с разными клавишами.

...