2

Let's say I have this TypeScript code:

enum EVENT {
  FIRST_EVENT = "game:first_encounter",
}

const EventHandler: keyof typeof EVENT = {
    [EVENT.FIRST_EVENT]: (data: any) => {
        console.log(`Hello there, ${data.blue_player}`);
    }
}

const data = { blue_player: 'Blue McBlue', event: EVENT.FIRST_EVENT}
const eventName: EVENT = data.event;

EventHandler[eventName](data);

I have an error with EventHandler near the top.

Type '{ "game:first_encounter": (data: any) => void; }' is not assignable to type '"FIRST_EVENT"'

I also have an error of with eventName on the last line with:

Element implicitly has an 'any' type because index expression is not of type 'number'

https://www.typescriptlang.org/play?#code/KYOwrgtgBAogajAcgFSgbwFBSgMQJIBKAysgPrxKoC8UARAOYCGEwAXAGYCWATgM4AupUAGMA9mBD9g3WgBoMAXwwYxIAbABuofgAlGIACYAbaaygBrYAE9R7KPysAHYLc3aoNTNmwBtCigA6fGIyf2QAXTMACgNGfkYzfSsASg8APnQsb2zVXlETAKNReiiAAx1gIyL7AAtpYFkoABI0WPiAgCMjMGBSRyNGK2kFUuSAbizsJSUVUTV+KDbGD3QoLp6+gaHuMwByACFu4CgAWWFDnt3G4C1JMzCgwhJyBBQlXIWb7URmNlhX6iLOKMAJfSQTDAwW66fTGaQ+MH8H4scIxYHjIA

See what I mean? How can I correctly type this?

6
  • 2
    The type declaration in const EventHandler: keyof typeof Event seems incomplete, and refers to the DOM Event type not to EVENT. What did you actually mean there? Commented Jan 17, 2022 at 21:20
  • Try omitting the type annotation and find out what TypeScript infers as the the type of the EventHandler variable. Commented Jan 17, 2022 at 21:22
  • @Bergi Yeah, that's on me. I updated it Commented Jan 17, 2022 at 21:24
  • Thanks. Still it's not quite clear what you are trying to do. Can you describe in words the type that EventHandler should have? Commented Jan 17, 2022 at 21:27
  • 1
    @Bergi presumably it's the (common) pattern of mapping actions to functions (using a unique key for actions which is what the enum is for I assume?) Commented Jan 17, 2022 at 21:30

1 Answer 1

2

Let's start typing EventHandler and figure this out.

First of all it's an object:

type EventHandlerType = {};

It has keys that are EVENTs

// Create a mapped type mapping events to anything
type EventHandlerType = { [e in EVENT]: any }

Every value is a function that takes a data parameter and returns void

// Create a mapped type mapping events to anything
type EventHandlerType = { [e in EVENT]: (data: any) => void };

Which should work in your code:

const EventHandler: { [key in EVENT]: (data: any) => void } = {
    [EVENT.FIRST_EVENT]: (data: any) => {
        console.log(`Hello there, ${data.blue_player}`);
    }
}

This is made simpler with the built in utility type Record which exists for these cases:


const EventHandler: Record<EVENT, (data: any) => void> = {
    [EVENT.FIRST_EVENT]: (data: any) => {
        console.log(`Hello there, ${data.blue_player}`);
    }
}

See mapped types in the docs for more examples and a drill down.

Sign up to request clarification or add additional context in comments.

8 Comments

I'd prefer Record<EVENT, (data: {blue_player: string}) => void>
@Bergi I considered (since it's what I use in my actual code) - I'll add that in, though didactically it has less value.
I see now. I understand. Is there a partial one? Some EVENTS might be abstracted out to other files where I can import them and spread them within the EventHandler...otherwise I am getting Property '"EVENT.SECOND_EVENT"' is missing in type
@GreenMtn you can put a question mark in front of a type to make it optional (so in this case { [e in EVENT]?: ...). There is a helper for that as well (called, Partial<>). So in your case it'd just be wrapping the whole type in Partial<>.
I see. Yeah it makes sense. Then in EventHandler[eventName](data) I get: Cannot invoke an object which is possibly 'undefined'
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.