Typesafe CustomEvents
Lets you easily set up typesafe
CustomEvent
channels in Typescript.
Install
💾 ~0.67KB minified, no additional dependencies.
npm i typesafe-custom-events
You can also alternatively copy/paste the source directly from here.
Use
You can create typesafe custom events by setting up a new channel.
Creating a new channel
import { CustomEventChannel } from "typesafe-custom-events";
type Toast = {
title: string;
status: "info" | "error";
};
const toastChannel = new CustomEventChannel<Toast>();
By passing a generic, you enforce a certain type for the custom event messages.
Listening for new events
// Start listening for events
const unsubscribe = toastChannel.subscribe((event) => {
// Do something when channel receives event
});
// Stop listening for events
unsubscribe();
Sending events
toastChannel.send({
title: "Foobar",
status: "info",
});
Reference
You can create a new CustomEventChannel
using the following arguments:
const name: string = "custom-channel-name"; // optional
const options: {
target?: EventTarget; // default: `globalThis`
} = {}; // optional
const channel = new CustomEventChannel(name, options);
A CustomEventChannel
instance contains the following properties:
- send
(event: T) => void
Send event to channel - subscribe
(onEvent: ((onEvent) => void)) => UnsubscribeFunction
Subscribes to the given channel - name
string
The channel name used to send and receive CustomEvents. - id
string
A unique identifier for the channel - subscriberCount
number
The current number of subscribers to the channel
Source
The following is the entire library source, if you prefer - you can copy/paste this into a file in your project.
const PREFIX = "tsce";
let i = 0;
const generateId = () => {
i++;
return `${i}`;
};
type UnsubscribeFunction = () => void;
type Options = {
target?: EventTarget;
};
export class CustomEventChannel<T> {
constructor(name?: string, opts?: Options) {
this.id = generateId();
this.name = name ?? `${PREFIX}-${this.id}`;
this.target = opts?.target ?? globalThis;
}
/** Target for emitting CustomEvent */
target: EventTarget;
/** Name of the CustomEvent and channel */
name: string;
/** Unique identifier for channel */
id: string;
/** Total amount of subscribers */
subscriberCount: number = 0;
/** Sends a new event to channel subscribers */
send(args: T) {
if (args === undefined) return;
this.target.dispatchEvent(
new CustomEvent(this.name, { detail: args })
);
}
/** Subscribes to events from channel */
subscribe(onEvent: (event: T) => any): UnsubscribeFunction {
const listener = (e: Event) => {
const event = e as Event & {
detail?: T;
};
if (event.detail !== undefined) {
onEvent(event.detail);
}
};
this.target.addEventListener(this.name, listener);
this.subscriberCount++;
return () => {
this.target.removeEventListener(this.name, listener);
this.subscriberCount--;
};
}
}