I am just wondering if in TypeScript you can define custom events on your classes or interfaces?
What would this look like?
TypeScript supports object-oriented programming features like classes, interfaces, etc. A class in terms of OOP is a blueprint for creating objects. A class encapsulates data for the object. Typescript gives built in support for this concept called class.
Classes in TypeScript, like JavaScript, are a special syntax for its prototypical inheritance model, which is comparable to inheritance in class-based object-oriented languages. Classes are just special functions added to ES6 that are meant to mimic the class keyword from these other languages.
How about this simplified event to be used as a property? Stronger typing of the owning class and no inheritance requirement:
interface ILiteEvent<T> { on(handler: { (data?: T): void }) : void; off(handler: { (data?: T): void }) : void; } class LiteEvent<T> implements ILiteEvent<T> { private handlers: { (data?: T): void; }[] = []; public on(handler: { (data?: T): void }) : void { this.handlers.push(handler); } public off(handler: { (data?: T): void }) : void { this.handlers = this.handlers.filter(h => h !== handler); } public trigger(data?: T) { this.handlers.slice(0).forEach(h => h(data)); } public expose() : ILiteEvent<T> { return this; } }
used like so:
class Security{ private readonly onLogin = new LiteEvent<string>(); private readonly onLogout = new LiteEvent<void>(); public get LoggedIn() { return this.onLogin.expose(); } public get LoggedOut() { return this.onLogout.expose(); } // ... onLogin.trigger('bob'); } function Init() { var security = new Security(); var loggedOut = () => { /* ... */ } security.LoggedIn.on((username?) => { /* ... */ }); security.LoggedOut.on(loggedOut); // ... security.LoggedOut.off(loggedOut); }
Improvements?
A gist for this
The NPM package Strongly Typed Events for TypeScript (GitHub) implements 3 types of events: IEvent<TSender, TArgs>
, ISimpleEvent<TArgs>
and ISignal
. This makes it easier to use the right kind of event for your project. It also hides the dispatch method from your event, as good information hiding should do.
Event Types / Interfaces - The definitions of the events:
interface IEventHandler<TSender, TArgs> { (sender: TSender, args: TArgs): void } interface ISimpleEventHandler<TArgs> { (args: TArgs): void } interface ISignalHandler { (): void; }
Example - This example shows how the 3 types of events can be implemented using a ticking clock:
class Clock { //implement events as private dispatchers: private _onTick = new SignalDispatcher(); private _onSequenceTick = new SimpleEventDispatcher<number>(); private _onClockTick = new EventDispatcher<Clock, number>(); private _ticks: number = 0; constructor(public name: string, timeout: number) { window.setInterval( () => { this.Tick(); }, timeout); } private Tick(): void { this._ticks += 1; //trigger event by calling the dispatch method and provide data this._onTick.dispatch(); this._onSequenceTick.dispatch(this._ticks); this._onClockTick.dispatch(this, this._ticks); } //expose the events through the interfaces - use the asEvent //method to prevent exposure of the dispatch method: public get onTick(): ISignal { return this._onTick.asEvent(); } public get onSequenceTick() : ISimpleEvent<number>{ return this._onSequenceTick.asEvent(); } public get onClockTick(): IEvent<Clock, number> { return this._onClockTick.asEvent(); } }
Usage - It can be used like this:
let clock = new Clock('Smu', 1000); //log the ticks to the console clock.onTick.subscribe(()=> console.log('Tick!')); //log the sequence parameter to the console clock.onSequenceTick.subscribe((s) => console.log(`Sequence: ${s}`)); //log the name of the clock and the tick argument to the console clock.onClockTick.subscribe((c, n) => console.log(`${c.name} ticked ${n} times.`))
Read more here: On events, dispatchers and lists (a general explanation of the system)
Tutorials
I've written a few tutorials on the subject:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With