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;