cyberwombat
cyberwombat

Reputation: 40084

Only allowing some enums in Typescript

I suppose this has been asked before but I am having trouble phrasing it to find a solutions so apologies in advance.

I have an enum type:

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

I have a function that in turn accepts a function. The function it accepts has a type EventHandler

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

The type definition for EventHandler is:

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

Now the functions that I pass are themselves limited in what events they can handle. For example:

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

Doing the above does not work and TS yields error:

wrapEventFn(actionHandler) // actionHandler is underlined w error 

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 

How can I fix this?

Playground

Upvotes: 1

Views: 157

Answers (1)

Andrew Shepherd
Andrew Shepherd

Reputation: 45252

Instead of enum, use String Literal Types

type Events = 'foo' | 'bar'

Now make EventHandler take a generic object that is assignable to type Events:

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

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

Now we can create your type which is a known subset of the available events:

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

Now we can call it like so:

wrapEventFn<SpecializedEvents>(actionHandler);

As it turns out, we don't even need to state the generic type, the typescript compiler can infer it. It's much more readable to just write this:

wrapEventFn(actionHandler);

To demonstrate this works, we can attempt to pass an incompatible type in:

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


wrapEventFn(wrongActionHandler);

The error this produces reads as follows:

Arguments of type '(event: CompletelyTheWrongType) => Promise' is not assignable to parameter of type 'EventHandler'...

playground link

Upvotes: 2

Related Questions