npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

type-safe-event-bus

v1.0.0

Published

Framework agnostic, fully typed event bus

Readme

The Event Bus is meant to provide a type-safe, framework-agnostic way to handle events in your application.

Usage example

import EventBus from "type-safe-event-bus";

// React to an event
EventBus.$on("viewed_page", (event) => {
  externalAnalytics.trackPageView({
    pageName: event.page,
    userId: event.userId,
  });
});

// Emit an event
EventBus.$emit("viewed_page", {
  page: "home",
  userId: "12345",
});

First-class TypeScript support

EventBus is designed to work seamlessly with TypeScript, providing type safety for both event emission and listening.

Declare event types and payloads

First, you need to declare the event types and their payloads. You can do this by extending the EventTypesPayloads interface in your TypeScript declaration file. This will allow you to define the events and their payloads in a type-safe manner.

// types/type-safe-event-bus.d.ts

// Declare events you want to emit by extending EventTypesPayloads
declare module "type-safe-event-bus" {
  interface EventTypesPayloads {
    foo: string;
    bar: number;
    baz: { userId: string; page: string };
  }
}

Emit events with type-safety

Now, your events are type-safe when you emit them:

import EventBus from "type-safe-event-bus";

EventBus.$emit("foo", "bar"); // OK
EventBus.$emit("foo", 123); // Error: Argument of type 'number' is not assignable to parameter of type 'string'.

EventBus.$emit("bar", "baz"); // Bar is not a defined event, so TypeScript will throw an error | Error: Argument of type '"bar"' is not assignable to parameter of type 'keyof EventTypesPayloads'.

Listen to events with type-safety

The events are also type-safe when you listen to them:

import EventBus from "type-safe-event-bus";

// The "event" parameter is inferred to be a string automatically
EventBus.$on("foo", (event) => {
  console.log(event); // event is a string
});

Listen to multiple events with type-safety

You can import the helper type ListenersMap to create a type-safe map of listeners for multiple events. This is useful when you want to register multiple listeners at once.

// listeners/Analytics.ts
import EventBus, { type ListenersMap } from "type-safe-event-bus";

const analytics: ListenersMap = {
  // The "event" parameter is inferred to be a string automatically, because we use ListenersMap
  foo: (event) => {
    return typeof hello === "string"; // Will always be true
  },

  bar: (event) => {
    return typeof hello === "number"; // Will always be true
  },

  // The full payload is inferred to be { userId: string; page: string }
  baz: ({ userId, page }) => {
    return typeof userId === "string" && typeof page === "string"; // Will always be true
  },
};

// Register the listeners with the event bus
eventBus.$onMany(analytics);

Built in listeners

Console log

This will log all events emitted, including the payloads, to the console. This is useful for debugging and understanding the flow of events in your application.

To register the console listener, you can use the ConsoleListener class provided by the event bus. This will automatically log all events emitted to the console.

import EventBus, {ConsoleListener} from "type-safe-event-bus";

// Register the Console listener with the event bus
eventBus.$onMany(ConsoleListener);

Create your own event bus instance

A full example of how to create your own event bus instance and use it with custom events.

Generally, you should use the singleton instance and not create your own bus instance.

Creating your own instance will not use the type-safe-event-bus singleton, but rather create a new instance of the EventBus class. This is useful if you want to create a separate event bus for a specific module or component in your application.

// src/MyEventBus.ts
import { EventBus, type ListenersMap } from "type-safe-event-bus"; // NOTE the import of the class {EventBus} (in the curly braces), which imports the class itself. Do NOT use the singleton instance (EventBus, e.g. the default import without the curly braces), which would import the sigleton instance of the EventBus class.

// You can declare custom events like this:
declare module "type-safe-event-bus" {
  interface EventTypesPayloads {
    foo: string;
  }
}
const eventBus = new EventBus();

// To add a listener, first define it:
const analytics: ListenersMap = {
  foo: (text) => {
    return typeof text === "string"; // Will always be true
  },
};

// Then create the listener:
eventBus.$onMany(analytics);

// Alternative ways to add listeners:
// eventBus.addListeners(analytics); // This is the same as the above line
// new Listeners(analytics); // This is the same as the above line

// Text is correctly seen as a string here by TypeScript and your IDE
eventBus.$on("foo", (text) => {
  console.log("foo", text);
});

// It is suggested you export the event bus instance so you can use it in other parts of your application
export default eventBus;