Fully type-checked EventEmitter with zero runtime overhead
npx @tessl/cli install tessl/npm-tiny-typed-emitter@2.1.00
# Tiny Typed Emitter
1
2
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.
3
4
## Package Information
5
6
- **Package Name**: tiny-typed-emitter
7
- **Package Type**: npm
8
- **Language**: TypeScript
9
- **Installation**: `npm install tiny-typed-emitter`
10
11
## Core Imports
12
13
```typescript
14
import { TypedEmitter } from "tiny-typed-emitter";
15
```
16
17
For CommonJS:
18
19
```javascript
20
const { TypedEmitter } = require("tiny-typed-emitter");
21
```
22
23
For type definitions:
24
25
```typescript
26
import { TypedEmitter, ListenerSignature, DefaultListener } from "tiny-typed-emitter";
27
```
28
29
## Basic Usage
30
31
```typescript
32
import { TypedEmitter } from "tiny-typed-emitter";
33
34
// Define event signatures
35
interface MyEvents {
36
'data': (payload: string, timestamp: number) => void;
37
'error': (error: Error) => void;
38
'complete': () => void;
39
}
40
41
// Create typed emitter
42
const emitter = new TypedEmitter<MyEvents>();
43
44
// Add listeners with full type safety
45
emitter.on('data', (payload, timestamp) => {
46
console.log(`Received: ${payload} at ${timestamp}`);
47
});
48
49
emitter.on('error', (error) => {
50
console.error('Error occurred:', error.message);
51
});
52
53
// Emit events with type checking
54
emitter.emit('data', 'Hello World', Date.now());
55
emitter.emit('error', new Error('Something went wrong'));
56
emitter.emit('complete');
57
```
58
59
## Architecture
60
61
Tiny Typed Emitter is built around a single core concept:
62
63
- **TypedEmitter Class**: A generic wrapper around Node.js EventEmitter that adds TypeScript type safety
64
- **Zero Runtime Overhead**: Simply re-exports the native EventEmitter with enhanced type definitions
65
- **Generic Type System**: Uses TypeScript generics to map event names to their listener function signatures
66
- **Full EventEmitter Compatibility**: All standard EventEmitter methods are available with enhanced typing
67
68
## Capabilities
69
70
### TypedEmitter Class
71
72
A generic class that wraps Node.js EventEmitter with compile-time type safety for event names and listener signatures.
73
74
```typescript { .api }
75
class TypedEmitter<L extends ListenerSignature<L> = DefaultListener> {
76
static defaultMaxListeners: number;
77
78
// Event listener management
79
addListener<U extends keyof L>(event: U, listener: L[U]): this;
80
prependListener<U extends keyof L>(event: U, listener: L[U]): this;
81
prependOnceListener<U extends keyof L>(event: U, listener: L[U]): this;
82
removeListener<U extends keyof L>(event: U, listener: L[U]): this;
83
removeAllListeners(event?: keyof L): this;
84
once<U extends keyof L>(event: U, listener: L[U]): this;
85
on<U extends keyof L>(event: U, listener: L[U]): this;
86
off<U extends keyof L>(event: U, listener: L[U]): this;
87
88
// Event emission
89
emit<U extends keyof L>(event: U, ...args: Parameters<L[U]>): boolean;
90
91
// Event introspection
92
eventNames<U extends keyof L>(): U[];
93
listenerCount(type: keyof L): number;
94
listeners<U extends keyof L>(type: U): L[U][];
95
rawListeners<U extends keyof L>(type: U): L[U][];
96
97
// Configuration
98
getMaxListeners(): number;
99
setMaxListeners(n: number): this;
100
}
101
```
102
103
**Usage Examples:**
104
105
```typescript
106
import { TypedEmitter } from "tiny-typed-emitter";
107
108
// Define events interface
109
interface FileWatcherEvents {
110
'file-changed': (filename: string, stats: { size: number; modified: Date }) => void;
111
'error': (error: Error) => void;
112
'started': () => void;
113
}
114
115
// Create typed emitter instance
116
const watcher = new TypedEmitter<FileWatcherEvents>();
117
118
// Type-safe listener registration
119
watcher.on('file-changed', (filename, stats) => {
120
// TypeScript knows filename is string and stats has size/modified properties
121
console.log(`File ${filename} changed. Size: ${stats.size}, Modified: ${stats.modified}`);
122
});
123
124
// Type-safe event emission
125
watcher.emit('file-changed', 'config.json', {
126
size: 1024,
127
modified: new Date()
128
});
129
130
// Class extension example
131
class FileWatcher extends TypedEmitter<FileWatcherEvents> {
132
constructor() {
133
super();
134
}
135
136
start() {
137
this.emit('started');
138
}
139
}
140
```
141
142
### Event Listener Management
143
144
Methods for adding, removing, and managing event listeners with full type safety.
145
146
```typescript { .api }
147
// Add listener to end of listeners array
148
addListener<U extends keyof L>(event: U, listener: L[U]): this;
149
150
// Add listener to beginning of listeners array
151
prependListener<U extends keyof L>(event: U, listener: L[U]): this;
152
153
// Add one-time listener to beginning of listeners array
154
prependOnceListener<U extends keyof L>(event: U, listener: L[U]): this;
155
156
// Remove specific listener
157
removeListener<U extends keyof L>(event: U, listener: L[U]): this;
158
159
// Remove all listeners for event (or all events if no event specified)
160
removeAllListeners(event?: keyof L): this;
161
162
// Add one-time listener
163
once<U extends keyof L>(event: U, listener: L[U]): this;
164
165
// Add listener (alias for addListener)
166
on<U extends keyof L>(event: U, listener: L[U]): this;
167
168
// Remove listener (alias for removeListener)
169
off<U extends keyof L>(event: U, listener: L[U]): this;
170
```
171
172
### Event Emission
173
174
Emit events with type-safe arguments based on the event signature.
175
176
```typescript { .api }
177
/**
178
* Emit an event with type-safe arguments
179
* @param event - Event name (must be key of L)
180
* @param args - Arguments matching the event's listener signature
181
* @returns true if event had listeners, false otherwise
182
*/
183
emit<U extends keyof L>(event: U, ...args: Parameters<L[U]>): boolean;
184
```
185
186
### Event Introspection
187
188
Methods for inspecting current listeners and event state.
189
190
```typescript { .api }
191
// Get array of registered event names
192
eventNames<U extends keyof L>(): U[];
193
194
// Get count of listeners for specific event
195
listenerCount(type: keyof L): number;
196
197
// Get copy of listeners array for event
198
listeners<U extends keyof L>(type: U): L[U][];
199
200
// Get copy of listeners array including wrappers
201
rawListeners<U extends keyof L>(type: U): L[U][];
202
```
203
204
### Configuration
205
206
Methods for configuring the emitter's behavior.
207
208
```typescript { .api }
209
// Get current maximum listeners limit
210
getMaxListeners(): number;
211
212
// Set maximum number of listeners (returns this for chaining)
213
setMaxListeners(n: number): this;
214
215
// Static property for default maximum listeners
216
static defaultMaxListeners: number;
217
```
218
219
## Types
220
221
```typescript { .api }
222
// Maps event names to their listener function signatures
223
type ListenerSignature<L> = {
224
[E in keyof L]: (...args: any[]) => any;
225
};
226
227
// Default listener signature for dynamic event names
228
type DefaultListener = {
229
[k: string]: (...args: any[]) => any;
230
};
231
```
232
233
## Advanced Usage
234
235
### Generic Events Interface
236
237
Use generic interfaces for reusable event patterns:
238
239
```typescript
240
import { TypedEmitter, ListenerSignature } from "tiny-typed-emitter";
241
242
interface DataEvents<T> {
243
'data': (item: T) => void;
244
'batch': (items: T[]) => void;
245
'complete': () => void;
246
}
247
248
class DataProcessor<T> extends TypedEmitter<DataEvents<T>> {
249
process(item: T) {
250
this.emit('data', item);
251
}
252
}
253
254
// Usage with specific type
255
const stringProcessor = new DataProcessor<string>();
256
stringProcessor.on('data', (str) => console.log(str.toUpperCase()));
257
```
258
259
### Compatible Subclasses with Different Events
260
261
Create compatible subclasses that introduce different events:
262
263
```typescript
264
import { TypedEmitter, ListenerSignature } from "tiny-typed-emitter";
265
266
class Animal<E extends ListenerSignature<E> = {}> extends TypedEmitter<{spawn: () => void} & E> {
267
constructor() {
268
super();
269
}
270
}
271
272
class Frog extends Animal<{jump: () => void}> {
273
jump() {
274
this.emit('jump');
275
}
276
}
277
278
class Bird extends Animal<{fly: () => void}> {
279
fly() {
280
this.emit('fly');
281
}
282
}
283
284
// Compatible usage
285
const animals: Animal[] = [new Frog(), new Bird()];
286
animals.forEach(animal => {
287
animal.on('spawn', () => console.log('Animal spawned'));
288
});
289
```