strict-event-emitter-types
Advanced tools
Comparing version 1.2.0 to 2.0.0
{ | ||
"name": "strict-event-emitter-types", | ||
"version": "1.2.0", | ||
"version": "2.0.0", | ||
"description": "Strictly and safely type any EventEmitter-like interface on any object", | ||
@@ -5,0 +5,0 @@ "main": "dist/src/index.js", |
## Typed Event Emitter | ||
**NOTE: REQUIRES TYPESCRIPT 2.8 SUPPORT FOR CONDITIONAL TYPES** | ||
**NOTE: REQUIRES TYPESCRIPT 3.0** | ||
A TypeScript library for strongly typed event emitters in 0 kB. Works with any kind of EventEmitter. | ||
A TypeScript library for strongly typed event emitters in 0 kB. Declare events using a simple interface mapping event names to their payloads to get stricter versions of `emit`, `on`, and other common EventEmitter APIs. Works with any kind of EventEmitter. | ||
### Installing | ||
### Installation | ||
@@ -13,3 +13,3 @@ ``` | ||
### Usage | ||
### Example | ||
@@ -19,29 +19,59 @@ ```ts | ||
// grab your event emitter | ||
import { EventEmitter } from 'events'; | ||
// define your events | ||
interface Events { | ||
newValue: number, | ||
done: void // an event with no payload | ||
request: (request: Request, response: Response) => void; | ||
done: void; | ||
} | ||
// Create a typed event emitter | ||
// grab an event emitter | ||
import { EventEmitter } from 'events'; | ||
// Create strict event emitter types | ||
let ee: StrictEventEmitter<EventEmitter, Events> = new EventEmitter; | ||
// now enjoy your strongly typed EventEmitter API! | ||
ee.on('newValue', x => x); // x is contextually typed to number | ||
// now enjoy your strongly typed EventEmitter API!🎉 | ||
ee.on('somethingElse'); // Error: unknown event | ||
ee.on('done', x => x); // Error: 'done' does not have a payload | ||
ee.emit('newValue', 'hello!'); // Error: incorrect payload | ||
ee.emit('newValue'); // Error: forgotten payload | ||
ee.on('request', (r, resp) => ... ); | ||
// r and resp are contextually typed to Request and Response | ||
ee.on('somethingElse'); | ||
// Error: unknown event | ||
ee.on('done', x => x); | ||
// Error: The 'done' event does not have a payload | ||
ee.emit('request', new Request()); | ||
// Error: missing event payload (the response) | ||
ee.emit('request', new Request(), false); | ||
// Error: incorrect payload type | ||
``` | ||
### StrictEventEmitter<TEmitterType, TEventRecord, TEmitRecord> | ||
### Usage | ||
#### Event Records | ||
Event records are interfaces or object types that map event names to the event's payload types. In the following example, three events are declared: | ||
```ts | ||
interface Events { | ||
req: (request: Request, response: Response) => void; | ||
done: void; | ||
conn: Connection; | ||
} | ||
``` | ||
Each event shows one of three ways to type the event payloads: | ||
1. **Function type:** Parameters are the event payload. The return type is ignored. | ||
1. **`void`:** A shortcut for an event with no payload, i.e. `() => void` | ||
1. **Anything else:** A shortcut for an event with one payload, for example `(p: number) => void` can be written as just `number`. | ||
#### StrictEventEmitter<TEmitterType, TEventRecord, TEmitRecord> | ||
The default export. A generic type that takes three type parameters: | ||
1. *TEmitterType*: Your EventEmitter type (e.g. node's EventEmitter or socket.io socket) | ||
2. *TEventRecord*: A type mapping event names to event payloads | ||
3. *TEmitRecord*: Optionally, a similar type mapping things you can emit. | ||
1. _TEmitterType_: Your EventEmitter type (e.g. node's EventEmitter or socket.io socket) | ||
2. _TEventRecord_: A type mapping event names to event payloads | ||
3. _TEmitRecord_: Optionally, a similar type mapping things you can emit. | ||
@@ -69,5 +99,20 @@ The third parameter is handy when typing web sockets where client and server can listen to and emit different events. For example, if you are using socket.io: | ||
##### Usage with Subclasses | ||
To subclass an EventEmitter you need to cast the base EventEmitter to the strict EventEmitter before extending: | ||
```ts | ||
type MyEmitter = StrictEventEmitter<EventEmitter, Events>; | ||
class MyEventEmitter extends (EventEmitter as { new(): MyEmitter }) { | ||
doEmit() { | ||
this.emit(...); // strict | ||
} | ||
} | ||
``` | ||
### StrictBroadcast<TStrictEventEmitter> | ||
A type for a function which takes (and strictly checks) an emit event and a payload. *TStrictEventEmitter* is the event emitter type instantiated from StrictEventEmitter. | ||
A type for a function which takes (and strictly checks) an emit event and a payload. _TStrictEventEmitter_ is the event emitter type instantiated from StrictEventEmitter. | ||
Useful for broadcast abstractions. It is not possible to contextually type assigments to this type, so your declarations will look something like this: | ||
@@ -78,7 +123,10 @@ | ||
const broadcast: StrictBroadcast<ServerSocket> = function (event: string, payload?: any) { | ||
const broadcast: StrictBroadcast<ServerSocket> = function( | ||
event: string, | ||
payload?: any | ||
) { | ||
// ... | ||
} | ||
}; | ||
``` | ||
Note that the loose types for event and payload only apply inside the broadcast function (consumers will see a much stricter signature). Declaring more precise parameter types or narrowing using type guards would allow strongly-typed dispatching to emitters. |
@@ -0,1 +1,2 @@ | ||
declare const assignmentCompatibilityHack: unique symbol; | ||
export declare type MatchingKeys<TRecord, TMatch, K extends keyof TRecord = keyof TRecord> = K extends (TRecord[K] extends TMatch ? K : never) ? K : never; | ||
@@ -8,17 +9,25 @@ export declare type VoidKeys<Record> = MatchingKeys<Record, void>; | ||
} | ||
export declare type OverriddenMethods<TEventRecord, TEmitRecord = TEventRecord, EventVK extends VoidKeys<TEventRecord> = VoidKeys<TEventRecord>, EventNVK extends Exclude<keyof TEventRecord, EventVK> = Exclude<keyof TEventRecord, EventVK>, EmitVK extends VoidKeys<TEmitRecord> = VoidKeys<TEmitRecord>, EmitNVK extends Exclude<keyof TEmitRecord, EmitVK> = Exclude<keyof TEmitRecord, EmitVK>> = { | ||
on<P extends EventNVK>(event: P, listener: (m: TEventRecord[P], ...args: any[]) => void): any; | ||
on<P extends EventVK>(event: P, listener: () => void): any; | ||
addListener<P extends EventNVK>(event: P, listener: (m: TEventRecord[P], ...args: any[]) => void): any; | ||
addListener<P extends EventVK>(event: P, listener: () => void): any; | ||
addEventListener<P extends EventNVK>(event: P, listener: (m: TEventRecord[P], ...args: any[]) => void): any; | ||
addEventListener<P extends EventVK>(event: P, listener: () => void): any; | ||
removeListener<P extends EventVK | EventNVK>(event: P, listener: Function): any; | ||
once<P extends EventNVK>(event: P, listener: (m: TEventRecord[P], ...args: any[]) => void): any; | ||
once<P extends EventVK>(event: P, listener: () => void): any; | ||
emit<P extends EmitNVK>(event: P, request: TEmitRecord[P]): any; | ||
emit<P extends EmitVK>(event: P): any; | ||
export declare type ReturnTypeOfMethod<T> = T extends (...args: any[]) => any ? ReturnType<T> : void; | ||
export declare type ReturnTypeOfMethodIfExists<T, S extends string> = S extends keyof T ? ReturnTypeOfMethod<T[S]> : void; | ||
export declare type InnerEEMethodReturnType<T, TValue, FValue> = T extends (...args: any[]) => any ? ReturnType<T> extends void | undefined ? FValue : TValue : FValue; | ||
export declare type EEMethodReturnType<T, S extends string, TValue, FValue = void> = S extends keyof T ? InnerEEMethodReturnType<T[S], TValue, FValue> : FValue; | ||
declare type ListenerType<T> = [T] extends [(...args: infer U) => any] ? U : [T] extends [void] ? [] : [T]; | ||
export declare type OverriddenMethods<TEmitter, TEventRecord, TEmitRecord = TEventRecord> = { | ||
on<P extends keyof TEventRecord, T>(this: T, event: P, listener: (...args: ListenerType<TEventRecord[P]>) => void): EEMethodReturnType<TEmitter, 'on', T>; | ||
on(event: typeof assignmentCompatibilityHack, listener: (...args: any[]) => any): void; | ||
addListener<P extends keyof TEventRecord, T>(this: T, event: P, listener: (...args: ListenerType<TEventRecord[P]>) => void): EEMethodReturnType<TEmitter, 'addListener', T>; | ||
addListener(event: typeof assignmentCompatibilityHack, listener: (...args: any[]) => any): void; | ||
addEventListener<P extends keyof TEventRecord, T>(this: T, event: P, listener: (...args: ListenerType<TEventRecord[P]>) => void): EEMethodReturnType<TEmitter, 'addEventListener', T>; | ||
addEventListener(event: typeof assignmentCompatibilityHack, listener: (...args: any[]) => any): void; | ||
removeListener<P extends keyof TEventRecord, T>(this: T, event: P, listener: (...args: any[]) => any): EEMethodReturnType<TEmitter, 'removeListener', T>; | ||
removeListener(event: typeof assignmentCompatibilityHack, listener: (...args: any[]) => any): void; | ||
removeEventListener<P extends keyof TEventRecord, T>(this: T, event: P, listener: (...args: any[]) => any): EEMethodReturnType<TEmitter, 'removeEventListener', T>; | ||
removeEventListener(event: typeof assignmentCompatibilityHack, listener: (...args: any[]) => any): void; | ||
once<P extends keyof TEventRecord, T>(this: T, event: P, listener: (...args: ListenerType<TEventRecord[P]>) => void): EEMethodReturnType<TEmitter, 'once', T>; | ||
once(event: typeof assignmentCompatibilityHack, listener: (...args: any[]) => any): void; | ||
emit<P extends keyof TEmitRecord, T>(this: T, event: P, ...args: ListenerType<TEmitRecord[P]>): EEMethodReturnType<TEmitter, 'emit', T>; | ||
emit(event: typeof assignmentCompatibilityHack, ...args: any[]): void; | ||
}; | ||
export declare type OverriddenKeys = keyof OverriddenMethods<any, any, any>; | ||
export declare type StrictEventEmitter<TEmitterType, TEventRecord, TEmitRecord = TEventRecord, UnneededMethods extends Exclude<OverriddenKeys, keyof TEmitterType> = Exclude<OverriddenKeys, keyof TEmitterType>, NeededMethods extends Exclude<OverriddenKeys, UnneededMethods> = Exclude<OverriddenKeys, UnneededMethods>> = TypeRecord<TEmitterType, TEventRecord, TEmitRecord> & Pick<TEmitterType, Exclude<keyof TEmitterType, OverriddenKeys>> & Pick<OverriddenMethods<TEventRecord, TEmitRecord>, NeededMethods>; | ||
export declare type StrictEventEmitter<TEmitterType, TEventRecord, TEmitRecord = TEventRecord, UnneededMethods extends Exclude<OverriddenKeys, keyof TEmitterType> = Exclude<OverriddenKeys, keyof TEmitterType>, NeededMethods extends Exclude<OverriddenKeys, UnneededMethods> = Exclude<OverriddenKeys, UnneededMethods>> = TypeRecord<TEmitterType, TEventRecord, TEmitRecord> & Pick<TEmitterType, Exclude<keyof TEmitterType, OverriddenKeys>> & Pick<OverriddenMethods<TEmitterType, TEventRecord, TEmitRecord>, NeededMethods>; | ||
export default StrictEventEmitter; | ||
@@ -25,0 +34,0 @@ export declare type NoUndefined<T> = T extends undefined ? never : T; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
10323
8
50
129