@meta-utils/events
v2.0.0
Published
Interface and implementation for EventTarget and Event
Downloads
20
Readme
events
This package contains pre-implemented events that you can just glue to your existing class like a breeze.
class MyClass extends EventTarget {}
let myInstance = new MyClass();
myInstance.addEventListener('load', () => console.log('hooray!'));
myInstance.dispatchEvent('load'); // hooray!
Key features
- Extensive type checking
- Standards compilant [1]
- Can be both
extends
'ed andimplements
'ed - Can be used in both TypeScript and pure JavaScript
- Support for one-time events using promises
One-time events
Sometimes you need to wait until a component is ready before you can continue in your code. Apart from the classic addEventListener
, you can leverage the promisified method once
which lets you keep your code clean and flat:
class Component extends EventTarget {
constructor() {
doThings();
this.dispatchEvent('loaded', { status: 'ready' });
}
}
const comp = new Component();
const event = await comp.once('loaded');
event.status === 'ready'
Implementing
Sometimes you need your class to inherit from something different than EventTarget
. Then you can use the static method factories which are shipped with this library. In JavaScript you can do it like this:
class MyClass extends DifferentClass {}
MyClass.prototype.addEventListener = EventTarget.addEventListener();
MyClass.prototype.removeEventListener = EventTarget.removeEventListener();
MyClass.prototype.dispatchEvent = EventTarget.dispatchEvent();
MyClass.prototype.once = EventTarget.once();
And in TypeScript like this:
class MyClass
extends DifferentClass
implements EventTarget<MyEvents>
{
public addEventListener = EventTarget.addEventListener<MyEvents>();
public removeEventListener = EventTarget.removeEventListener<MyEvents>();
public dispatchEvent = EventTarget.dispatchEvent<MyEvent>();
public once = EventTarget.once<MyEvent>();
}
Type checking options
If you use TypeScript, you can choose from three type checking options.
Minimum checking
If you use the non-generic EventTarget
, you'll get all the functionality without having to worry about any typing at all.
class Person extends EventTarget
{
kill()
{
let options = { somethingOther: 42 };
let eventName: string = 'died';
this.dispatchEvent(eventName, options);
}
}
let john = new Person();
john.addEventListener('anything you want', (e) => {
e.target; // EventTarget
e.timeStamp // number
e.somethingOther // any
});
Loose checking
If you want to only allow for certain events, you can pass a union of string literals as the type argument of EventTarget
to make something like EventTarget<'load'|'unload'>
.
class Person extends EventTarget<'died'>
{
kill()
{
let options = { somethingOther: 42 };
this.dispatchEvent('died', options);
this.dispatchEvent('was born', options); // Error
}
}
let jane = new Person();
myInstance.addEventListener('died', (e) => {
e.name; // 'died'
e.target; // EventTarget<'died'>
e.somethingOther // any
});
Strict checking
If you want to get maximum type safety and cool editor auto-completion, you should describe the events with an interface and then pass the interface to EventTarget
.
interface PersonEvents
{
died: {};
killed: { byWhom: Person };
}
class Person extends EventTarget<PersonEvents>
{
kill(murderer: Person)
{
this.dispatchEvent('died');
this.dispatchEvent('killed', { byWhom: murderer });
}
letDie()
{
this.dispatchEvent('died');
}
}
let alex = new Person();
alex.addEventListener('killed', (e) => {
e.name; // 'killed'
e.target; // still only EventTarget, not Person, but I'm working on it
e.byWhom; // Person
});
alex.addEventListener('died', (e) => {
e.byWhom; // error
});