or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/npm-tiny-typed-emitter

Fully type-checked EventEmitter with zero runtime overhead

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/tiny-typed-emitter@2.1.x

To install, run

npx @tessl/cli install tessl/npm-tiny-typed-emitter@2.1.0

index.mddocs/

Tiny Typed Emitter

Tiny Typed Emitter provides a fully type-checked EventEmitter wrapper with zero runtime overhead. It enables developers to define strongly-typed event interfaces and ensures compile-time type safety for event emission and listener registration while maintaining full compatibility with Node.js EventEmitter.

Package Information

  • Package Name: tiny-typed-emitter
  • Package Type: npm
  • Language: TypeScript
  • Installation: npm install tiny-typed-emitter

Core Imports

import { TypedEmitter } from "tiny-typed-emitter";

For CommonJS:

const { TypedEmitter } = require("tiny-typed-emitter");

For type definitions:

import { TypedEmitter, ListenerSignature, DefaultListener } from "tiny-typed-emitter";

Basic Usage

import { TypedEmitter } from "tiny-typed-emitter";

// Define event signatures
interface MyEvents {
  'data': (payload: string, timestamp: number) => void;
  'error': (error: Error) => void;
  'complete': () => void;
}

// Create typed emitter
const emitter = new TypedEmitter<MyEvents>();

// Add listeners with full type safety
emitter.on('data', (payload, timestamp) => {
  console.log(`Received: ${payload} at ${timestamp}`);
});

emitter.on('error', (error) => {
  console.error('Error occurred:', error.message);
});

// Emit events with type checking
emitter.emit('data', 'Hello World', Date.now());
emitter.emit('error', new Error('Something went wrong'));
emitter.emit('complete');

Architecture

Tiny Typed Emitter is built around a single core concept:

  • TypedEmitter Class: A generic wrapper around Node.js EventEmitter that adds TypeScript type safety
  • Zero Runtime Overhead: Simply re-exports the native EventEmitter with enhanced type definitions
  • Generic Type System: Uses TypeScript generics to map event names to their listener function signatures
  • Full EventEmitter Compatibility: All standard EventEmitter methods are available with enhanced typing

Capabilities

TypedEmitter Class

A generic class that wraps Node.js EventEmitter with compile-time type safety for event names and listener signatures.

class TypedEmitter<L extends ListenerSignature<L> = DefaultListener> {
  static defaultMaxListeners: number;
  
  // Event listener management
  addListener<U extends keyof L>(event: U, listener: L[U]): this;
  prependListener<U extends keyof L>(event: U, listener: L[U]): this;
  prependOnceListener<U extends keyof L>(event: U, listener: L[U]): this;
  removeListener<U extends keyof L>(event: U, listener: L[U]): this;
  removeAllListeners(event?: keyof L): this;
  once<U extends keyof L>(event: U, listener: L[U]): this;
  on<U extends keyof L>(event: U, listener: L[U]): this;
  off<U extends keyof L>(event: U, listener: L[U]): this;
  
  // Event emission
  emit<U extends keyof L>(event: U, ...args: Parameters<L[U]>): boolean;
  
  // Event introspection
  eventNames<U extends keyof L>(): U[];
  listenerCount(type: keyof L): number;
  listeners<U extends keyof L>(type: U): L[U][];
  rawListeners<U extends keyof L>(type: U): L[U][];
  
  // Configuration
  getMaxListeners(): number;
  setMaxListeners(n: number): this;
}

Usage Examples:

import { TypedEmitter } from "tiny-typed-emitter";

// Define events interface
interface FileWatcherEvents {
  'file-changed': (filename: string, stats: { size: number; modified: Date }) => void;
  'error': (error: Error) => void;
  'started': () => void;
}

// Create typed emitter instance
const watcher = new TypedEmitter<FileWatcherEvents>();

// Type-safe listener registration
watcher.on('file-changed', (filename, stats) => {
  // TypeScript knows filename is string and stats has size/modified properties
  console.log(`File ${filename} changed. Size: ${stats.size}, Modified: ${stats.modified}`);
});

// Type-safe event emission
watcher.emit('file-changed', 'config.json', { 
  size: 1024, 
  modified: new Date() 
});

// Class extension example
class FileWatcher extends TypedEmitter<FileWatcherEvents> {
  constructor() {
    super();
  }
  
  start() {
    this.emit('started');
  }
}

Event Listener Management

Methods for adding, removing, and managing event listeners with full type safety.

// Add listener to end of listeners array
addListener<U extends keyof L>(event: U, listener: L[U]): this;

// Add listener to beginning of listeners array  
prependListener<U extends keyof L>(event: U, listener: L[U]): this;

// Add one-time listener to beginning of listeners array
prependOnceListener<U extends keyof L>(event: U, listener: L[U]): this;

// Remove specific listener
removeListener<U extends keyof L>(event: U, listener: L[U]): this;

// Remove all listeners for event (or all events if no event specified)
removeAllListeners(event?: keyof L): this;

// Add one-time listener
once<U extends keyof L>(event: U, listener: L[U]): this;

// Add listener (alias for addListener)
on<U extends keyof L>(event: U, listener: L[U]): this;

// Remove listener (alias for removeListener)
off<U extends keyof L>(event: U, listener: L[U]): this;

Event Emission

Emit events with type-safe arguments based on the event signature.

/**
 * Emit an event with type-safe arguments
 * @param event - Event name (must be key of L)
 * @param args - Arguments matching the event's listener signature
 * @returns true if event had listeners, false otherwise
 */
emit<U extends keyof L>(event: U, ...args: Parameters<L[U]>): boolean;

Event Introspection

Methods for inspecting current listeners and event state.

// Get array of registered event names
eventNames<U extends keyof L>(): U[];

// Get count of listeners for specific event
listenerCount(type: keyof L): number;

// Get copy of listeners array for event
listeners<U extends keyof L>(type: U): L[U][];

// Get copy of listeners array including wrappers
rawListeners<U extends keyof L>(type: U): L[U][];

Configuration

Methods for configuring the emitter's behavior.

// Get current maximum listeners limit
getMaxListeners(): number;

// Set maximum number of listeners (returns this for chaining)
setMaxListeners(n: number): this;

// Static property for default maximum listeners
static defaultMaxListeners: number;

Types

// Maps event names to their listener function signatures
type ListenerSignature<L> = {
  [E in keyof L]: (...args: any[]) => any;
};

// Default listener signature for dynamic event names
type DefaultListener = {
  [k: string]: (...args: any[]) => any;
};

Advanced Usage

Generic Events Interface

Use generic interfaces for reusable event patterns:

import { TypedEmitter, ListenerSignature } from "tiny-typed-emitter";

interface DataEvents<T> {
  'data': (item: T) => void;
  'batch': (items: T[]) => void;
  'complete': () => void;
}

class DataProcessor<T> extends TypedEmitter<DataEvents<T>> {
  process(item: T) {
    this.emit('data', item);
  }
}

// Usage with specific type
const stringProcessor = new DataProcessor<string>();
stringProcessor.on('data', (str) => console.log(str.toUpperCase()));

Compatible Subclasses with Different Events

Create compatible subclasses that introduce different events:

import { TypedEmitter, ListenerSignature } from "tiny-typed-emitter";

class Animal<E extends ListenerSignature<E> = {}> extends TypedEmitter<{spawn: () => void} & E> {
  constructor() {
    super();
  }
}

class Frog extends Animal<{jump: () => void}> {
  jump() {
    this.emit('jump');
  }
}

class Bird extends Animal<{fly: () => void}> {
  fly() {
    this.emit('fly');
  }
}

// Compatible usage
const animals: Animal[] = [new Frog(), new Bird()];
animals.forEach(animal => {
  animal.on('spawn', () => console.log('Animal spawned'));
});