Home > database >  Typescript event emitter typings
Typescript event emitter typings

Time:10-17

I'm implementing event emitter but ran into a problem with typescript typing. Please tell me how to make this work.

export type IEventMap = {
    "mount": () => void;
    "mounted": () => void;
    "unmount": () => void;
    "unmounted": () => void;
    "test1": (stringArg: string) => void;
    "test2": (numberArg: number) => void;
};

export type IEventNames = keyof IEventMap;
export type IEventFn<K extends IEventNames> = IEventMap[K];
export type IEventListeners = { [K in IEventNames]?: Set<IEventFn<K>> };

export default class Events {

    private readonly listeners: IEventListeners = {};

    public on <K extends IEventNames> (eventName: K, callback: IEventFn<K>) {

        if (!this.listeners[eventName]) {
            this.listeners[eventName] = new Set();
        }

        this.listeners[eventName].add(callback);

    }

    public off <K extends IEventNames> (eventName: K, callback: IEventFn<K>) {

        if (this.listeners[eventName]) {
            this.listeners[eventName].delete(callback);
        }

    }

    public emit <K extends IEventNames> (eventName: K, ...args: Parameters<IEventFn<K>>) {

        if (this.listeners[eventName]) {
            this.listeners[eventName].forEach(callback => callback(...args));
        }

    }

}

// test

const events = new Events();

events.on("mount", () => {
    console.log("mount");
});

events.on("test1", arg => {
    console.log(arg);
});

events.on("test2", arg => {
    console.log(arg);
});

events.emit("mount");
events.emit("test1", "test string");
events.emit("test2", 1);

Errors

Type 'Set<IEventFn>' is not assignable to type 'IEventListeners[K]'. Type 'Set<IEventFn>' is not assignable to type 'Set<() => void> & Set<() => void> & Set<() => void> & Set<() => void> & Set<(stringArg: string) => void> & Set<(numberArg: number) => void>'. Type 'Set<IEventFn>' is not assignable to type 'Set<() => void>'. Type 'IEventFn' is not assignable to type '() => void'. Type '(() => void) | (() => void) | (() => void) | (() => void) | ((stringArg: string) => void) | ((numberArg: number) => void)' is not assignable to type '() => void'. Type '(stringArg: string) => void' is not assignable to type '() => void'.

Object is possibly 'undefined'. Object is possibly 'undefined'. Object is possibly 'undefined'.

A spread argument must either have a tuple type or be passed to a rest parameter.

Playground link

CodePudding user response:

  1. interchangeable callbacks should actually be rest-args, so
export type IEventFn<K extends IEventNames> = (...a: Parameters<IEventMap[K]>) => void;

fixes half of errors

  1. ts doesn't remember type if if by whatever reason, just use ?.
this.listeners[eventName]?.delete(callback);
  1. ts doesn't want do assign mismatching types, type down so types match
let l: { [KK in K]?: Set<IEventFn<KK>> } = this.listeners;
(l[eventName] ??= new Set<IEventFn<K>>()).add(callback);

playground

  • Related