From 75c7e191ffa65d2cfccf2dda4e4395427fb979f6 Mon Sep 17 00:00:00 2001 From: Vlad Frangu Date: Wed, 3 Jul 2024 04:15:51 +0300 Subject: [PATCH] fix: correct strictness of types and overloads for certain methods --- src/index.ts | 136 +++++++++++++++++++++++++++++-------------------- tests/types.ts | 34 ++++++++++++- 2 files changed, 113 insertions(+), 57 deletions(-) diff --git a/src/index.ts b/src/index.ts index 4fe6a0f..83c35b4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -152,8 +152,15 @@ export class AsyncEventEmitter< listener: AsyncEventEmitterListenerForEvent, K>, ): this; - public addListener(eventName: string | symbol, listener: (...args: any[]) => void): this; - public addListener(eventName: string | symbol, listener: (...args: any[]) => void) { + public addListener( + eventName: K, + listener: AsyncEventEmitterListenerForEvent, K>, + ): this; + + public addListener( + eventName: K, + listener: AsyncEventEmitterListenerForEvent, K>, + ): this { validateListener(listener); const wrapped = this._wrapListener(eventName, listener, false); @@ -168,8 +175,15 @@ export class AsyncEventEmitter< listener: AsyncEventEmitterListenerForEvent, K>, ): this; - public on(eventName: string | symbol, listener: (...args: any[]) => void): this; - public on(eventName: string | symbol, listener: (...args: any[]) => void) { + public on( + eventName: K, + listener: AsyncEventEmitterListenerForEvent, K>, + ): this; + + public on( + eventName: K, + listener: AsyncEventEmitterListenerForEvent, K>, + ): this { return this.addListener(eventName, listener); } @@ -178,9 +192,15 @@ export class AsyncEventEmitter< listener: AsyncEventEmitterListenerForEvent, K>, ): this; - public once(eventName: string | symbol, listener: (...args: any[]) => void): this; + public once( + eventName: K, + listener: AsyncEventEmitterListenerForEvent, K>, + ): this; - public once(eventName: string | symbol, listener: (...args: any[]) => void): this { + public once( + eventName: K, + listener: AsyncEventEmitterListenerForEvent, K>, + ): this { validateListener(listener); const wrapped = this._wrapListener(eventName, listener, true); @@ -195,7 +215,10 @@ export class AsyncEventEmitter< listener: AsyncEventEmitterListenerForEvent, K>, ): this; - public removeListener(eventName: string | symbol, listener: (...args: any[]) => void): this; + public removeListener( + eventName: K, + listener: AsyncEventEmitterListenerForEvent, K>, + ): this; public removeListener(eventName: string | symbol, listener: (...args: any[]) => void): this { validateListener(listener); @@ -261,9 +284,15 @@ export class AsyncEventEmitter< listener: AsyncEventEmitterListenerForEvent, K>, ): this; - public off(eventName: string | symbol, listener: (...args: any[]) => void): this; + public off( + eventName: K, + listener: AsyncEventEmitterListenerForEvent, K>, + ): this; - public off(eventName: string | symbol, listener: (...args: any[]) => void): this { + public off( + eventName: K, + listener: AsyncEventEmitterListenerForEvent, K>, + ): this { return this.removeListener(eventName, listener); } @@ -324,11 +353,11 @@ export class AsyncEventEmitter< const listeners = events[event]; if (typeof listeners === 'function') { - this.removeListener(event, listeners); + this.removeListener(event, listeners as any); } else if (listeners !== undefined) { // LIFO order for (let i = listeners.length - 1; i >= 0; i--) { - this.removeListener(event, listeners[i]); + this.removeListener(event, listeners[i] as any); } } @@ -353,9 +382,13 @@ export class AsyncEventEmitter< eventName: K, ): AsyncEventEmitterListenerForEvent, K>[]; - public listeners(eventName: string | symbol): ((...args: any[]) => void)[]; + public listeners( + eventName: K, + ): AsyncEventEmitterListenerForEvent, K>[]; - public listeners(eventName: string | symbol): ((...args: any[]) => void)[] { + public listeners( + eventName: K, + ): AsyncEventEmitterListenerForEvent, K>[] { const eventList = this._events[eventName]; if (eventList === undefined) { @@ -363,15 +396,18 @@ export class AsyncEventEmitter< } if (typeof eventList === 'function') { - return [eventList.listener ?? eventList]; + return [eventList.listener ?? eventList] as AsyncEventEmitterListenerForEvent< + AsyncEventEmitter, + K + >[]; } - const ret = arrayClone(eventList) as ((...args: any[]) => void)[]; + const ret = arrayClone(eventList) as AsyncEventEmitterListenerForEvent, K>[]; for (let i = 0; i < ret.length; ++i) { const orig = (ret[i] as Listener).listener; if (typeof orig === 'function') { - ret[i] = orig; + ret[i] = orig as AsyncEventEmitterListenerForEvent, K>; } } @@ -382,9 +418,13 @@ export class AsyncEventEmitter< eventName: K, ): AsyncEventEmitterInternalListenerForEvent, K>[]; - public rawListeners(eventName: string | symbol): Listener[]; + public rawListeners( + eventName: K, + ): AsyncEventEmitterInternalListenerForEvent, K>[]; - public rawListeners(eventName: string | symbol): Listener[] { + public rawListeners( + eventName: K, + ): AsyncEventEmitterInternalListenerForEvent, K>[] { const eventList = this._events[eventName]; if (eventList === undefined) { @@ -392,10 +432,10 @@ export class AsyncEventEmitter< } if (typeof eventList === 'function') { - return [eventList]; + return [eventList] as AsyncEventEmitterInternalListenerForEvent, K>[]; } - return arrayClone(eventList); + return arrayClone(eventList) as AsyncEventEmitterInternalListenerForEvent, K>[]; } public emit( @@ -403,7 +443,10 @@ export class AsyncEventEmitter< ...args: GetAsyncEventEmitterEventParameters, K> ): boolean; - public emit(eventName: string | symbol, ...args: any[]): boolean; + public emit( + eventName: K, + ...args: GetAsyncEventEmitterEventParameters, K> + ): boolean; public emit(eventName: string | symbol, ...args: any[]): boolean { let doError = eventName === 'error'; @@ -503,9 +546,15 @@ export class AsyncEventEmitter< listener: AsyncEventEmitterListenerForEvent, K>, ): this; - public prependListener(eventName: string | symbol, listener: (...args: any[]) => void): this; + public prependListener( + eventName: K, + listener: AsyncEventEmitterListenerForEvent, K>, + ): this; - public prependListener(eventName: string | symbol, listener: (...args: any[]) => void): this { + public prependListener( + eventName: K, + listener: AsyncEventEmitterListenerForEvent, K>, + ): this { validateListener(listener); const wrapped = this._wrapListener(eventName, listener, false); @@ -520,9 +569,15 @@ export class AsyncEventEmitter< listener: AsyncEventEmitterListenerForEvent, K>, ): this; - public prependOnceListener(eventName: string | symbol, listener: (...args: any[]) => void): this; + public prependOnceListener( + eventName: K, + listener: AsyncEventEmitterListenerForEvent, K>, + ): this; - public prependOnceListener(eventName: string | symbol, listener: (...args: any[]) => void): this { + public prependOnceListener( + eventName: K, + listener: AsyncEventEmitterListenerForEvent, K>, + ): this { validateListener(listener); const wrapped = this._wrapListener(eventName, listener, true); @@ -639,26 +694,12 @@ export class AsyncEventEmitter< options?: AbortableMethods, ): Promise>; - public static async once< - Emitter extends AsyncEventEmitter, - EventNames extends Record = Emitter extends AsyncEventEmitter ? Events - : Record, - EventName extends PropertyKey = keyof EventNames | keyof AsyncEventEmitterPredefinedEvents, - EventResult extends unknown[] = GetAsyncEventEmitterEventParameters, - >(emitter: Emitter, eventName: EventName, options: AbortableMethods): Promise; - public static async once( emitter: AsyncEventEmitter, eventName: string | symbol, options?: AbortableMethods, ): Promise; - public static async once( - emitter: AsyncEventEmitter, - eventName: string | symbol, - options: AbortableMethods, - ): Promise; - public static async once( emitter: AsyncEventEmitter, eventName: string | symbol, @@ -720,29 +761,12 @@ export class AsyncEventEmitter< options?: AbortableMethods, ): AsyncGenerator, void>; - public static on< - Emitter extends AsyncEventEmitter, - EventNames extends Record = Emitter extends AsyncEventEmitter ? Events - : Record, - EventName extends PropertyKey = keyof EventNames | keyof AsyncEventEmitterPredefinedEvents, - >( - emitter: Emitter, - eventName: EventName, - options: AbortableMethods, - ): AsyncGenerator, void>; - public static on( emitter: AsyncEventEmitter, eventName: string | symbol, options?: AbortableMethods, ): AsyncGenerator; - public static on( - emitter: AsyncEventEmitter, - eventName: string | symbol, - options: AbortableMethods, - ): AsyncGenerator; - public static on( emitter: AsyncEventEmitter, eventName: string | symbol, diff --git a/tests/types.ts b/tests/types.ts index 7896a27..7a0d275 100644 --- a/tests/types.ts +++ b/tests/types.ts @@ -1,4 +1,8 @@ -import { AsyncEventEmitter, type GetAsyncEventEmitterEventParameters } from '../src'; +import { + AsyncEventEmitter, + type AsyncEventEmitterListenerForEvent, + type GetAsyncEventEmitterEventParameters, +} from '../src'; declare const t: AsyncEventEmitter<{ foo: [bar: string]; @@ -58,13 +62,35 @@ t.addListener('unknown', (arg1: 1) => {}); { t.emit('newListener', 'foo', () => {}); + t.emit('newListener', {}); + t.emit('foo', 'bar'); + t.emit('foo', true); + t.emit('baz', true, false); t.emit('mama mia', 'here we go again'); } +{ + const bound = t.emit.bind(t, 'newListener'); +} + +{ + const listener: AsyncEventEmitterListenerForEvent = (bar) => { + console.log(bar); + }; + + const listener2: AsyncEventEmitterListenerForEvent = (...args) => { + console.log(args); + }; + + t.removeListener('foo', listener); + + t.removeListener('foo', listener2); +} + { const eventCount = AsyncEventEmitter.listenerCount(t, 'baz'); @@ -74,6 +100,8 @@ t.addListener('unknown', (arg1: 1) => {}); { const oncePromise1 = AsyncEventEmitter.once(t, 'foo'); + const oncePromise1_1 = AsyncEventEmitter.once(t, 'foo', {}); + const oncePromise2 = AsyncEventEmitter.once(t, 'newListener'); const oncePromise3 = AsyncEventEmitter.once(t, 'bazzinga'); @@ -81,6 +109,8 @@ t.addListener('unknown', (arg1: 1) => {}); void AsyncEventEmitter.once(t, 'removeListener'); void AsyncEventEmitter.once(t, 'foo'); + + void AsyncEventEmitter.once(t, 'no dice'); } { @@ -110,6 +140,8 @@ declare const ee: AsyncEventEmitter; const events = ee.eventNames(); // ^? + + events.includes('owo'); } interface Events {