Strictly Typed Event Emitter Interface
Typed-emitter is a lightweight TypeScript-only package (zero runtime bytes) that provides a strictly typed interface for event emitters. It is currently stable at version 2.1.0, receiving minor updates for bug fixes and feature enhancements, with occasional breaking changes primarily around type constraints or specific integration points like RxJS. Unlike `@types/node` which offers loose typings, or `eventemitter3` which lacks strong argument typing, typed-emitter allows developers to define a precise `EventMap` for their emitter instances. This enables compile-time verification of event names and listener argument signatures, preventing common runtime errors. It's designed to be used with existing `EventEmitter` implementations, such as Node.js's built-in `events` module or browser-based alternatives, without providing its own implementation.
Common errors
-
Argument of type 'string' is not assignable to parameter of type 'Error'.
cause An event listener function's parameter type does not match the type defined in the `EventMap` for that event.fixAdjust the listener function signature (parameter types) to precisely match the `EventMap` definition for the corresponding event. -
Property 'myUndefinedEvent' does not exist on type 'TypedEmitter<MyEventMap>'.
cause Attempting to emit or listen to an event name that has not been explicitly defined in the `EventMap` provided to `TypedEmitter`.fixAdd the desired event name and its corresponding listener signature to your `EventMap` type definition. -
Type 'MyCustomEventMap' does not satisfy the constraint '{ [key: string]: (...args: any[]) => void; }'. Type 'MyCustomEventMap' is not assignable to type '{ [key: string]: (...args: any[]) => void; }'. Property 'invalidKey' is incompatible with index signature. Type 'string' is not assignable to type '(...args: any[]) => void'.cause Your custom event map type contains properties that are not functions, violating the `EventMap` constraint introduced in v2.0.0.fixEnsure that every property (event name) in your `EventMap` is defined as a function type (e.g., `eventName: (arg1: Type) => void`).
Warnings
- breaking Since v2.0.0, the `TypedEmitter` generic type parameter `Events` is strictly constrained to `EventMap` (`{ [key: string]: (...args: any[]) => void }`). Your event map types must conform to this structure, where all properties are functions.
- breaking With v2.1.0, a new `TypedEmitter` interface for RxJS compatibility was introduced, exported from `typed-emitter/rxjs`. If you were previously using custom workarounds or older patterns for RxJS `fromEvent` type inference, you may need to update your imports and usage to leverage the new `FromEvent` type.
- gotcha Prior to v1.4.0, the `package.json` `main` field incorrectly pointed to a TypeScript `.ts` file instead of the compiled JavaScript. While fixed in v1.4.0, older versions might cause resolution issues, build failures, or unexpected behavior in bundlers or environments that don't correctly process `.ts` files directly as a main entry point.
Install
-
npm install typed-emitter -
yarn add typed-emitter -
pnpm add typed-emitter
Imports
- TypedEmitter
import { TypedEmitter } from 'typed-emitter'import TypedEmitter from 'typed-emitter'
- FromEvent
import { FromEvent } from 'typed-emitter'import { FromEvent } from 'typed-emitter/rxjs' - Arguments
import Arguments from 'typed-emitter'
import { Arguments } from 'typed-emitter'
Quickstart
import EventEmitter from "events";
import TypedEmitter from "typed-emitter";
type MessageEvents = {
error: (error: Error) => void;
message: (body: string, from: string) => void;
statusUpdate: (status: 'online' | 'offline') => void;
};
const messageEmitter = new EventEmitter() as TypedEmitter<MessageEvents>;
// Good: TypeScript validates event name and argument types
messageEmitter.emit("message", "Hi there!", "no-reply@test.com");
messageEmitter.emit("statusUpdate", "online");
// TypeScript will catch type mistakes
// @ts-expect-error Argument of type 'boolean' is not assignable to parameter of type 'string'.
messageEmitter.emit("message", "Hi there!", true);
// @ts-expect-error Argument of type 'string' is not assignable to parameter of type '"online" | "offline"'.
messageEmitter.emit("statusUpdate", "connecting");
// Good: Type-safe listener registration
messageEmitter.on("error", (error: Error) => { console.error(error.message); });
// TypeScript will catch mistakes in listener signatures
// @ts-expect-error Argument of type 'string' is not assignable to parameter of type 'Error'.
messageEmitter.on("error", (error: string) => { /* ... */ });
console.log('Typed emitter setup successfully.');
// To prevent process from exiting immediately in Node.js
setInterval(() => {}, 1000);