The code provided below is currently working without any type errors:
type Events = { SOME_EVENT: number; OTHER_EVENT: string }
interface EventEmitter<EventTypes> {
on<K extends keyof EventTypes>(s: K, listener: (v: EventTypes[K]) => void);
}
declare const emitter: EventEmitter<Events>;
emitter.on('SOME_EVENT', (payload) => testNumber(payload));
emitter.on('OTHER_EVENT', (payload) => testString(payload));
function testNumber( value: number ) {}
function testString( value: string ) {}
However, I am interested in utilizing something similar to an enum
for autocompletion of the type names. This way, I can write the following instead of using string literals:
emitter.on(EventNames.SOME_EVENT, (payload) => testNumber(payload));
emitter.on(EventNames.OTHER_EVENT, (payload) => testString(payload));
I aim to maintain DRY principles, so I am exploring options to achieve this without duplicating all event names in a new type.
In JavaScript, specifically, I can easily implement the following:
const EventNames = [
'SOME_EVENT',
'OTHER_EVENT',
]
const Events = {}
for (const eventName of Events) {
Events[eventName] = eventName
}
// and then use it:
emitter.on(Events.SOME_EVENT, (payload) => testNumber(payload));
emitter.on(Events.OTHER_EVENT, (payload) => testString(payload));
Hence, in plain JS, I can generate enum-like objects without needing to resort to less DRY alternatives like:
const Events = {
SOME_EVENT: 'SOME_EVENT', // duplicates the names
OTHER_EVENT: 'OTHER_EVENT',
}
emitter.on(Events.SOME_EVENT, (payload) => testNumber(payload));
emitter.on(Events.OTHER_EVENT, (payload) => testString(payload));
My goal is to maximize DRYness in TypeScript by defining each event name only once while also associating a type with the event payloads.
One approach that is less DRY involves repeating the event names three times:
type Events = { SOME_EVENT: number; OTHER_EVENT: string }
const EventNames: {[k in keyof Events]: k} = {
SOME_EVENT: 'SOME_EVENT', OTHER_EVENT: 'OTHER_EVENT'
}
interface EventEmitter<EventTypes> {
on<K extends keyof EventTypes>(s: K, listener: (v: EventTypes[K]) => void);
}
declare const emitter: EventEmitter<Events>;
emitter.on(EventNames.SOME_EVENT, (payload) => testNumber(payload));
emitter.on(EventNames.OTHER_EVENT, (payload) => testString(payload));
function testNumber( value: number ) {}
function testString( value: string ) {}
Therefore, my query is: Is there a method to structure this in a way where I define each event only once along with their payload types?
If achieving this level of optimization with just one instance of each event name is not viable, what would be the best alternative approach?