2.0.1 • Published 2 years ago

static-emitter v2.0.1

Weekly downloads
-
License
MIT
Repository
github
Last release
2 years ago

npm package License Quality

Contents

Introduction

EventTarget with support for static type enforcement of events.

The default event target allows for any string as the event name, and the data as Event. So knowing/using type information is impossible without extra (unnecessary) validations, or manual casting (risky).

StaticEmitter is a wrapper of EventTarget that enforces event format via static type declarations. It also supports non-Event bodies, symbol events, and NodeJS-esque syntax (on(), emit()...).

A type-only wrapper of EventTarget is also available, StaticEventTarget.

StaticEmitter works in both NodeJS and the browser.

Install

npm i static-emitter

Example

import { type events, StaticEmitter } from 'static-emitter';

class MyEmitter extends StaticEmitter {
    declare public [events]: {
        foo: [boolean, number[]];
        bar: [string];
    }
}

const myEmitter = new StaticEmitter<{
    foo: number[];
    bar: string;
}>();
myEmitter.on('foo', nums => {
    console.log(nums); // typed as number[]
});
myEmitter.emit('foo', [123]); // success!

myEmitter.emit('bar', { wrong: null }); // Typescript error!

Usage

static-emitter is an ESM module. That means it must be imported. To load from a CJS module, use dynamic import const { StaticEmitter, StaticEventTarget } = await import('static-emitter');.

The StaticEventTarget is a type-cast of EventTarget. It provides no extra functionality in javascript-land. Therefore, it is highly recommended this class be used in Typescript to gain any benefit from static typing. It may be loaded separately via import { StaticEventTarget } from 'static-emitter/static-event-target';.

The StaticEmitter is an extension of StaticEventTarget. It includes support for symbol events, non-event bodies, and EventEmitter-esque syntax.

API

TypedEvent

Type-only casting of Event. Includes a generic parameter for setting the eventName.

This casting may be convenient for usage with raw StaticEventTarget.

import { TypedEvent } from 'static-emitter';
// Or solo
import { TypedEvent } from 'static-emitter/typed-event';

const myEvent: TypedEvent<'abc'> = new TypedEvent('abc');

CustomEvent

On Browser type-only casting of CustomEvent. Extends TypedEvent and includes an additional generic parameter for setting the detail. For non-null details, the detail option will be required.

On NodeJS a polyfill for CustomEvent, which does not natively exists.

import { CustomEvent } from 'static-emitter';
// Or solo
import { CustomEvent } from 'static-emitter/custom-event';

const myEvent: CustomEvent<'abc', 123> = new TypedEvent('abc', { detail: 123 });
const myNullEvent: CustomEvent<'foo', null> = new TypedEvent('foo');

events

A type-only symbol for declaring events on emitter extension. Not strictly required if event declarations only occur via generic parameters.

Note that events does not exist in javascript, and should only be used for event declarations.

Ensure any usage is paired with the typeof or declare keywords.

StaticEventTarget

A type-only extension of EventTarget. Events can be declared which enforce the allowed dispatch/listener events.

Events can be declared via generic parameters (recommended when instantiating/extending an EventTarget directly) or via the events property.

Events are declared as a key-value mapping of eventName and eventDetail.

When eventDetail is an Event-extension (e.g. MouseEvent) then the detail is left unchanged. When any other type, it is wrapped in a CustomEvent.

import { CustomEvent, events, StaticEventTarget } from 'static-emitter';
// Or solo
import { StaticEventTarget } from 'static-emitter/static-event-target';

class MyTarget extends StaticEventTarget<{
    click: MouseEvent;
}> {}

class MyExtendedTarget extends MyTarget {
    [events]: MyTarget[events] & {
        foo: 'bar';
    }
}
const myTarget = new MyExtendedTarget();

// Alternative
const myTarget = new StaticEventTarget<{
    click: MouseEvent;
    foo: 'bar';
}>();

myTarget.addEventListener('click', (mouseEvent: MouseEvent) => {...});
myTarget.addEventListener('foo', {
    handleEvent: (mouseEvent: CustomEvent<'foo', 'bar'>) => {...}),
});
// Typescript Error! Type mismatch.
myTarget.addEventListener('foo', (numEvent: CustomEvent<'foo', number>) => {...});

myTarget.dispatchEvent(new CustomEvent('foo', { detail: 'bar' }));
myTarget.dispatchEvent(new MouseEvent('click'));

StaticEmitter

Extension of StaticEventTarget with support for symbol events, non-event bodies, and EventEmitter-esque syntax.

The event declaration syntax of StaticEmitter is the same as StaticEventTarget, and any native events can be used the same way.

Note that symbol events and non-event bodies cannot use the native EventTarget methods. Similarly, any non-CustomEvent events cannot use the helper methods and must rely on EventTarget's native methods (type constraints will still be applied). The exception is off() which supports all event names/listeners.

Listener methods (on + addListener) will pass two parameters. The first is the parsed detail from the custom event, and the raw CustomEvent as the second parameter.

import { CustomEvent, StaticEmitter } from 'static-emitter';

const serverConnect = Symbol('server-connect');

const myTarget = new StaticEmitter<{
    click: MouseEvent;
    foo: 'bar';
    [serverConnect]: { port: number; timestamp: Date };
}>();

myTarget.on('foo', (bar: 'bar', nativeEvent: CustomEvent<'foo', 'bar'>) => {});
// Alias for `on()`
myTarget.addListener(serverConnect, (connectionDetails: { port: number; timestamp: Date }) => {});
// Typescript Error! Type mismatch.
myTarget.on('foo', (num: number) => {});
// Typescript Error! Native MouseEvent not supported.
myTarget.on('click', (mouseEvent: MouseEvent) => {});

// Remove listener immediately after first event
myTarget.once('foo', (bar: 'bar') => {});

myTarget.emit('foo', 'bar');
myTarget.emit(serverConnect, { port: 3000, timestamp: new Date() });
// Typescript Error! Type mismatch.
myTarget.emit('foo', 123);

// Example only, note this wouldn't actually "remove" anything, as this function was never added.
myTarget.off(serverConnect, () => {})
// Alias for `off()`. Supports native format.
myTarget.removeListener('foo', {
    handleEvent: (customEvent: CustomEvent<'foo', 'bar'>) => {},
});
// Supports native events
myTarget.off('click', (mouseEvent: MouseEvent) => {});
1.0.2

2 years ago

1.0.6

2 years ago

1.0.5

2 years ago

1.0.4

2 years ago

1.0.3

2 years ago

2.0.1

2 years ago

2.0.0

2 years ago

0.0.0

2 years ago