This is because
on('config', configHandler) // TSC ERROR HERE
expects as a second argument EventHandler
. Arguments of EventHandler
and configHandler
are not assignable to each other.
declare var config: Config;
declare var ev: Event;
config = ev // error
ev = config // error
Btw, Event
is defined in TS globals. It is a built-in type.
Are you sure you want to extend it by index signature?
In order to make it work, you should extend Event
type by Config
:
interface Event extends Config {
[key: string]: string;
}
Anyway, this seems to be not type safe.
Take a look on this example:
const enum Events {
foo = "foo",
bar = "bar",
baz = "baz",
}
/**
* Single sourse of true
*/
interface EventMap {
[Events.foo]: { foo: number };
[Events.bar]: { bar: string };
[Events.baz]: { baz: string[] };
}
type Values<T> = T[keyof T];
// credits goes to https://stackoverflow.com/a/50375286
type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (
k: infer I
) => void
? I
: never;
type EmitRecord = {
[P in keyof EventMap]: (name: P, data: EventMap[P]) => void;
};
type ListenRecord = {
[P in keyof EventMap]: (
name: P,
callback: (arg: EventMap[P]) => void
) => void;
};
type MakeOverloadings<T> = UnionToIntersection<Values<T>>;
type Emit = MakeOverloadings<EmitRecord>;
type Listen = MakeOverloadings<ListenRecord>;
const emit: Emit = <T,>(name: string, data: T) => {};
emit(Events.bar, { bar: "1" });
emit(Events.baz, { baz: ["1"] });
emit("unimplemented", { foo: 2 }); // expected error
const listen: Listen = (name: string, callback: (arg: any) => void) => {};
listen(Events.baz, (arg /* { baz: string[] } */) => {});
listen(Events.bar, (arg /* { bar: string } */) => {});
Above example is much safer.
Here, in my blog, you can find more explanation