typescript - Typesafe Event Handlers for Union Type -
i trying build lightweight event system using union types, can listen events 1 type in union. here have far (unfortunately, not utilize types):
class eventsystem { events: { [key: string]: { (event: eventtype) }[] }; constructor() { this.events = {}; } emit(key: string, event: eventtype): void { var arr = this.events[key]; (let = 0; < arr.length; ++i) { arr[i](event); } } on(key: string, callback: (event: eventtype) => void) { if (key in this.events) { this.events[key].push(callback); } else { this.events[key] = [callback]; } } } interface eventa { foo: number } interface eventb { bar: number baz: string } type eventtype = eventa | eventb const eventnames = { eventa: 'eventa', eventb: 'eventb' } let x = {foo: 2} eventa; let y = { bar: 4, baz: "test" } eventb; let es = new eventsystem(); es.on(eventnames.eventa, function (a: eventa) { console.log(a); }); //triggers on above es.emit(eventnames.eventa, x); //unfortunately, triggers on above es.emit(eventnames.eventa, y);
what want this:
let es = new eventsystem<eventtype>(); es.on<eventa>(function (a) { //a inferred eventa console.log(a); }); //triggers on above es.emit(x); //will not trigger on, since type not match es.emit(y); //type error, since number not in eventtype es.emit(4);
is possible in typescript? if not, there more typesafe approach doing? or better way in general type of behavior?
edit:
for now, doing following. adds lot of boilerplate eventsystem class (i have hundreds of message types), , makes api little ugly in opinion, @ least type safety. amount of duplicated code makes me think there must better way.
class eventsystem { events: {[p in eventnames]: { (event: eventtype) }[]} = { 'eventa': [], 'eventb': [] }; emiteventa(event: eventa): void { this.events['eventa'].foreach((eventfunc) => eventfunc(event)); } emiteventb(event: eventb): void { this.events['eventb'].foreach((eventfunc) => eventfunc(event)); } oneventa(callback: (event: eventa) => void) { this.events['eventa'].push(callback); } oneventb(callback: (event: eventb) => void) { this.events['eventb'].push(callback); } } interface eventa { foo: number } interface eventb { bar: number baz: string } type eventtype = eventa | eventb type eventnames = 'eventa' | 'eventb' let x = { foo: 2 } eventa; let y = { bar: 4, baz: "test" } eventb; let es = new eventsystem(); es.oneventa(function (a) { console.log(a); }); //triggers on above es.emiteventa(x); //correctly caught es.emiteventa(y);
yes, possible. here example code works in project:
class producerclass { private config; private logger; constructor(config: config, logger: logger); enqueue<t extends string, m extends object>(topic: t, message: m): promise<boolean>; connect(): void; } const producer = producerclass(config, logger); type topic = 'sometask' | 'someothertask'; interface message { objectid: number; } await producer.enqueue<topic, message>('sometask', { objectid: id });
Comments
Post a Comment