typing eventhandler in typescript

I came across this issue when I made a type definition file for the upcoming web-ble functionalities in chrome and edge browser (more about it).

the challenge#

The challenge is the following:

  • there are n-events we can register to trough a function addEventhandler
  • in the callback we get data back
  • depending on the event we register to, we expect a typed callback

untyped example:

MyObject.addEventhandler("eventA", myCallback, options);

function myCallback(event: any) {
  // do something here
}

solution#

mapped types#

Mapping Types are great if we want determine the type based on a single key.

type MyObject = {
  name: string;
  age: number;
};

function get<>();

define possible callbacks#

type MyEventsMap = {
  eventA: (event: EventDataA) => void;
  eventB: (event: EventDataB) => void;
};

full code example#

With direct implementation:

// define event data

type EventDataA = "EventDataA";
type EventDataB = "EventDataB";

// setup mapping

type MyEventsMap = {
  eventA: (event: EventDataA) => void;
  eventB: (event: EventDataB) => void;
};

// create callbacks

const handlerA: MyEventsMap["eventA"] = (event: EventDataA) => {};
const handlerB: MyEventsMap["eventB"] = (event: EventDataB) => {};

// function definition - possibility 1

function addEventhandler<K extends keyof MyEventsMap>(eventName: K, func: MyEventsMap[K]) {
  // implementation details
}

addEventhandler("eventA", (event: EventDataA) => {});
addEventhandler("eventA", handlerA);

Alternative to direct implementation trough typed arrow function:

// function definition implementing type - possibility 2

type MyTypeFunction<Type> = {
  <K extends keyof Type>(eventName: K, event: Type[K]): void;
};

const addEventhandler2: MyTypeFunction<MyEventsMap> = () => {
  // implementation details
};

addEventhandler2("eventB", (event: EventDataB) => {});
addEventhandler("eventB", handlerB);

Here is the full code example in action TS Playground Link