Reputation: 45500
I have a class that is just like Node's EventEmitter (it could even be the same class).
How do I type it in such a way that I can specify what the callback type is for each different event name?
For example, I'd like for even "foo"
to have a payload of type number
, and event "bar"
to have a payload of type string
, as in:
const emitter = /* get emitter from somewhere. And how would the type definition look like? */
emitter.on('foo', payload => {
/* type of payload is number, enforced by TypeScript */
testNumber('foo') // TS error, 'foo' is not a number
testNumber(payload) // it works, payload is a number
})
emitter.on('bar', payload => {
/* type of payload is string, enforced by TypeScript */
testString(5) // TS error, 5 is not a string
testString(payload) // it works, payload is a string
})
function testNumber( value: number ) {}
function testString( value: string ) {}
How would we define the EventEmitter declaration so that it is possible to define events and their types, and to then let users use those events with the correct type checking?
Maybe there's a way to define the type of EventEmitter such that when I create one I pass a type argument that contains all the expected types?
Is there a way to do it dynamically after creation?
Upvotes: 3
Views: 1693
Reputation: 27224
I'd start off with something like this:
interface Events {
foo: number
bar: string
}
interface EventEmitter<T> {
on<K extends keyof T>(s: K, listener: (v: T[K]) => void): T[K];
}
declare const emitter: EventEmitter<Events>;
emitter.on('foo', (payload: any) => {
testNumber(payload)
testString(payload)
});
emitter.on('bar', (payload: any) => {
testNumber(payload)
testString(payload)
});
function testNumber( value: number ) {}
function testString( value: string ) {}
Here's an example on TS Playground.
Upvotes: 3