Skip to content

Latest commit

 

History

History
134 lines (104 loc) · 3.26 KB

README.md

File metadata and controls

134 lines (104 loc) · 3.26 KB

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--;
    };
  }
}