Перегрузка различных функций в зависимости от универсального типа - PullRequest
1 голос
/ 28 сентября 2019

Как я могу обеспечить перегрузку функции, основанную на одном из аргументов, имеющих тип never?

type Message<T> = { id: string };

function publish<T>(message: Message<T>, payload: T) {
  // ...
}

Если T равен never, это означает, что сообщение никогда не имеетполезную нагрузку, и поэтому я не хочу, чтобы функция ожидала аргумент data.

Я перегружен функцией, чтобы предоставить альтернативную сигнатуру для случая never, что делает аргумент payload необязательным.

function publish(message: Message<never>, payload?: never): void

function publish<T>(message: Message<T>, payload: T): void {
  // ...
}

Это работает в случае Message<never>, но прерывает все другие вызовы:

let NeverMessage: Message<never> = { id: "never-message" };
let NumberMessage: Message<number> = { id: "number-message" };

publish(NeverMessage);
// All good!

publish(NumberMessage, 10);
// function publish(message: Message<never>, payload?: undefined): void
// Argument of type '10' is not assignable to parameter of type 'undefined'

Как я могу перегрузить определение, не делая payload необязательным для обеих подписей?

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

1 Ответ

2 голосов
/ 28 сентября 2019

Вы на самом деле не перегружаете функцию так, как вы думаете.Перегруженная функция имеет упорядоченный список сигнатур вызовов , которые видимы для вызывающих функций и не имеют реализации (они заканчиваются на ;, а не {...}), и один сигнатура реализации , которая видна для реализации функции.Якобы вы хотите, чтобы звонящие, а не просто реализация, видели подпись publish<T>(message: Message<T>, payload: T): void.Если это так, вам нужно сделать что-то вроде этого:

// call signatures
function publish(message: Message<never>, payload?: never): void;
function publish<T>(message: Message<T>, payload: T): void;

// implementation signature
function publish<T>(message: Message<T>, payload: T): void {
  // ...
}

Это должно исправить вашу проблему, как указано.


Кроме того, обратите внимание, что это не рекомендуется иметь общий тип, например

type Message<T> = { id: string };

, где параметр типа не используется.Система типов TypeScript в основном структурная , а не номинальная, что означает, что если два типа имеют одинаковую структуру , то они имеют одинаковый тип, даже если вы используете разные имена для ссылки на них.В этом случае Message<never> и Message<number> оба { id: string }, и, следовательно, они одного типа.

Возможно, что компилятор будет обрабатывать выражение, явно напечатанное как Message<never>, в отличие от выражения, явно напечатанного как Message<number>, и что ваши перегрузки будут вести себя так, как вы хотите.Но нет никакой гарантии, что это всегда будет работать, и странные вещи могут случиться, когда этого не произойдет.

Обычная мудрость здесь - использовать параметр типа где-то в структуре вашеготип.Даже что-то вроде

type Message<T> = { id: string; __messageType?: T };

иногда достаточно, чтобы все заработало, хотя во время выполнения не должно быть никакого свойства __messagetype.

И вам также может понадобиться быть осторожным с подтипами и супертипами, так как даже в приведенном выше определении Message<T> есть Message<never>, являющийся подтипом любого Message<T>, то есть вы можете вызвать publish(NeverMessage, "hello there"); без ошибок,Значение NeverMessage будет восприниматься как действительное Message<"hello there">.Чтобы предотвратить , что , вам нужно сделать Message<T> инвариантом в T, что может быть достигнуто, если вы включили --strictFunctionTypes с помощьюсвойство функции, подобное этому:

type Message<T> = { id: string; __messageType?: (x: T) => T };

И вам также нужно расширить сигнатуру реализации:

function publish(message: Message<never>, payload?: never): void;
function publish<T>(message: Message<T>, payload: T): void;
function publish<T>(message: Message<T> | Message<never>, payload?: T): void {
  // ...
}

Это приведет к

publish(NeverMessage); // okay
publish(NumberMessage, 10); // okay
publish(NeverMessage, "hello there"); // error!

.... но я отвлекся, потому что ты не спрашивал об этом.10

Ответ на ваш главный вопрос: не забудьте добавить отдельную сигнатуру реализации.


Хорошо, надеюсь, это поможет;удачи!

Ссылка на код

...