Event Source/Handler helpers DOM like

A few simple generic classes to create event sources and handlers with the same feel like the DOM events.

type eventMap<T> = { [key in keyof T]?: { handle: string, callback: (params: T[key]) => any }[] }

type BaseEvents = {
}

class EventProvider<T = BaseEvents> {
    private eventMap: eventMap<T> = {}

    private _id: string = this.generateHandle()
    public get id(): string {
        return this._id
    }

    private generateHandle(): string {
        return `${new Date().getTime()}${Math.floor(Math.random() * 1000)}`
    }

    public invokeEvent<K extends keyof T>(event: K, params: T[K]): void {
        this.eventMap[event]?.forEach(c => { c.callback(params) })
    }

    public addEventListener<K extends keyof T>(event: K, callback: (params: T[K]) => any): string {
        if (!Object.keys(this.eventMap).includes(event as string))
            this.eventMap[event] = []

        const entry = { handle: this.generateHandle(), callback }

        this.eventMap[event]!.push(entry)

        return entry.handle
    }

    public removeEventListener(handle: string) {
        Object.getOwnPropertyNames(this.eventMap).forEach((eventNames: any) => {
            this.eventMap[eventNames as keyof T] = this.eventMap[eventNames as keyof T]!.filter(c => c.handle != handle)
        })
    }
}

Usage Example:

type TestClass1Events = {
    "loaded": { var: string }
    "unloaded": { var: number, var2: string }
}

class TestClass1 extends EventProvider<TestClass1Events> {
    private test() {
        this.invokeEvent("loaded", { var: "test" })
        this.invokeEvent("unloaded", { var: 1, var2: "test" })
    }
}

type TestClass2Events = TestClass1Events & {
    "loadedV2": { var: string, max: number }
    "unloadedV2": { var: number, max: string }
}

class TestClass2 extends EventProvider<TestClass2Events> {
    private test() {
        this.invokeEvent("loadedV2", { var: "test", max: 1 })
        this.invokeEvent("unloadedV2", { var: 1, max: "test" })
    }
}