CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-tiny-typed-emitter

Fully type-checked EventEmitter with zero runtime overhead

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

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'));
});
Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/tiny-typed-emitter@2.1.x
Publish Source
CLI
Badge
tessl/npm-tiny-typed-emitter badge