Разрешить только некоторые перечисления в Typescript

#typescript

#typescript

Вопрос:

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

У меня есть тип перечисления:

 enum Events {
 FOO = 'foo',
 BAR = 'bar'
}
  

У меня есть функция, которая, в свою очередь, принимает функцию. Функция, которую она принимает, имеет тип EventHandler

 export const wrapEventFn = async (fn: EventHandler): Promise<unknown> => {
...
  

Определение типа для EventHandler :

 type EventHandler = (event: Events) => Promise<void>
  

Теперь функции, которые я передаю, сами ограничены в том, какие события они могут обрабатывать. Например:

 export const actionHandler = async (
  event: Events.FOO
): Promise<void> => {...
  

Выполнение вышеуказанного не работает, и TS выдает ошибку:

 wrapEventFn(actionHandler) // actionHandler is underlined w error 
  

Ошибка

 Argument of type '(event: Events.FOO) => Promise<void>' is not assignable to parameter of type 'EventHandler'.
  Types of parameters 'event' and 'event' are incompatible.
    Type 'Events' is not assignable to type 
  

Как я могу это исправить?

Игровая площадка

Комментарии:

1. (event: Events.FOO): Promise<void> не удается обработать ни одно из Events них, кроме определенного — отсюда ошибка

2. Не могли бы вы добавить еще немного информации о том, что wrapEventFn делает?

3. Пожалуйста, взгляните на эту проблему: github.com/microsoft/TypeScript/issues/21998

4. Ошибка полностью верна. Ваш actionHandler обрабатывает только один из возможных Events , но определение EventHandler предполагает, что его можно вызвать с любым Event

Ответ №1:

Вместо enum этого используйте строковые литеральные типы

 type Events = 'foo' | 'bar'
  

Теперь заставьте EventHandler использовать универсальный объект, который можно присвоить типу Events :

 type EventHandler<T extends Events> = (event: T) => Promise<void>;

declare function wrapEventFn<T extends Events>(fn: EventHandler<T>): Promise<unknown>;
  

Теперь мы можем создать ваш тип, который является известным подмножеством доступных событий:

 type SpecializedEvents = 'foo'; 
declare function actionHandler(event: SpecializedEvents): Promise<void>;
  

Теперь мы можем назвать это так:

 wrapEventFn<SpecializedEvents>(actionHandler);
  

Как оказалось, нам даже не нужно указывать общий тип, компилятор typescript может вывести его. Гораздо удобнее просто написать это:

 wrapEventFn(actionHandler);
  

Чтобы продемонстрировать, что это работает, мы можем попытаться передать несовместимый тип в:

 type CompletelyTheWrongType = 'this' | 'is' | 'wrong';
declare function wrongActionHandler(event: CompletelyTheWrongType): Promise<void>;


wrapEventFn(wrongActionHandler);
  

Ошибка, которую это выдает, выглядит следующим образом:

Аргументы типа ‘(event: CompletelyTheWrongType) => Обещание’ не могут быть присвоены параметру типа ‘EventHandler’…

ссылка на игровую площадку