Skip to content

Commit

Permalink
fix: correct strictness of types and overloads for certain methods
Browse files Browse the repository at this point in the history
  • Loading branch information
vladfrangu committed Jul 3, 2024
1 parent 2f46e1a commit 75c7e19
Show file tree
Hide file tree
Showing 2 changed files with 113 additions and 57 deletions.
136 changes: 80 additions & 56 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,8 +152,15 @@ export class AsyncEventEmitter<
listener: AsyncEventEmitterListenerForEvent<AsyncEventEmitter<Events>, K>,
): this;

public addListener(eventName: string | symbol, listener: (...args: any[]) => void): this;
public addListener(eventName: string | symbol, listener: (...args: any[]) => void) {
public addListener<K extends string | symbol>(
eventName: K,
listener: AsyncEventEmitterListenerForEvent<AsyncEventEmitter<Events>, K>,
): this;

public addListener<K extends string | symbol>(
eventName: K,
listener: AsyncEventEmitterListenerForEvent<AsyncEventEmitter<Events>, K>,
): this {
validateListener(listener);

const wrapped = this._wrapListener(eventName, listener, false);
Expand All @@ -168,8 +175,15 @@ export class AsyncEventEmitter<
listener: AsyncEventEmitterListenerForEvent<AsyncEventEmitter<Events>, K>,
): this;

public on(eventName: string | symbol, listener: (...args: any[]) => void): this;
public on(eventName: string | symbol, listener: (...args: any[]) => void) {
public on<K extends string | symbol>(
eventName: K,
listener: AsyncEventEmitterListenerForEvent<AsyncEventEmitter<Events>, K>,
): this;

public on<K extends string | symbol>(
eventName: K,
listener: AsyncEventEmitterListenerForEvent<AsyncEventEmitter<Events>, K>,
): this {
return this.addListener(eventName, listener);
}

Expand All @@ -178,9 +192,15 @@ export class AsyncEventEmitter<
listener: AsyncEventEmitterListenerForEvent<AsyncEventEmitter<Events>, K>,
): this;

public once(eventName: string | symbol, listener: (...args: any[]) => void): this;
public once<K extends string | symbol>(
eventName: K,
listener: AsyncEventEmitterListenerForEvent<AsyncEventEmitter<Events>, K>,
): this;

public once(eventName: string | symbol, listener: (...args: any[]) => void): this {
public once<K extends string | symbol>(
eventName: K,
listener: AsyncEventEmitterListenerForEvent<AsyncEventEmitter<Events>, K>,
): this {
validateListener(listener);

const wrapped = this._wrapListener(eventName, listener, true);
Expand All @@ -195,7 +215,10 @@ export class AsyncEventEmitter<
listener: AsyncEventEmitterListenerForEvent<AsyncEventEmitter<Events>, K>,
): this;

public removeListener(eventName: string | symbol, listener: (...args: any[]) => void): this;
public removeListener<K extends string | symbol>(
eventName: K,
listener: AsyncEventEmitterListenerForEvent<AsyncEventEmitter<Events>, K>,
): this;

public removeListener(eventName: string | symbol, listener: (...args: any[]) => void): this {
validateListener(listener);
Expand Down Expand Up @@ -261,9 +284,15 @@ export class AsyncEventEmitter<
listener: AsyncEventEmitterListenerForEvent<AsyncEventEmitter<Events>, K>,
): this;

public off(eventName: string | symbol, listener: (...args: any[]) => void): this;
public off<K extends string | symbol>(
eventName: K,
listener: AsyncEventEmitterListenerForEvent<AsyncEventEmitter<Events>, K>,
): this;

public off(eventName: string | symbol, listener: (...args: any[]) => void): this {
public off<K extends string | symbol>(
eventName: K,
listener: AsyncEventEmitterListenerForEvent<AsyncEventEmitter<Events>, K>,
): this {
return this.removeListener(eventName, listener);
}

Expand Down Expand Up @@ -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);
}
}

Expand All @@ -353,25 +382,32 @@ export class AsyncEventEmitter<
eventName: K,
): AsyncEventEmitterListenerForEvent<AsyncEventEmitter<Events>, K>[];

public listeners(eventName: string | symbol): ((...args: any[]) => void)[];
public listeners<K extends string | symbol>(
eventName: K,
): AsyncEventEmitterListenerForEvent<AsyncEventEmitter<Events>, K>[];

public listeners(eventName: string | symbol): ((...args: any[]) => void)[] {
public listeners<K extends string | symbol>(
eventName: K,
): AsyncEventEmitterListenerForEvent<AsyncEventEmitter<Events>, K>[] {
const eventList = this._events[eventName];

if (eventList === undefined) {
return [];
}

if (typeof eventList === 'function') {
return [eventList.listener ?? eventList];
return [eventList.listener ?? eventList] as AsyncEventEmitterListenerForEvent<
AsyncEventEmitter<Events>,
K
>[];
}

const ret = arrayClone(eventList) as ((...args: any[]) => void)[];
const ret = arrayClone(eventList) as AsyncEventEmitterListenerForEvent<AsyncEventEmitter<Events>, 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<AsyncEventEmitter<Events>, K>;
}
}

Expand All @@ -382,28 +418,35 @@ export class AsyncEventEmitter<
eventName: K,
): AsyncEventEmitterInternalListenerForEvent<AsyncEventEmitter<Events>, K>[];

public rawListeners(eventName: string | symbol): Listener[];
public rawListeners<K extends string | symbol>(
eventName: K,
): AsyncEventEmitterInternalListenerForEvent<AsyncEventEmitter<Events>, K>[];

public rawListeners(eventName: string | symbol): Listener[] {
public rawListeners<K extends string | symbol>(
eventName: K,
): AsyncEventEmitterInternalListenerForEvent<AsyncEventEmitter<Events>, K>[] {
const eventList = this._events[eventName];

if (eventList === undefined) {
return [];
}

if (typeof eventList === 'function') {
return [eventList];
return [eventList] as AsyncEventEmitterInternalListenerForEvent<AsyncEventEmitter<Events>, K>[];
}

return arrayClone(eventList);
return arrayClone(eventList) as AsyncEventEmitterInternalListenerForEvent<AsyncEventEmitter<Events>, K>[];
}

public emit<K extends keyof Events | keyof AsyncEventEmitterPredefinedEvents>(
eventName: K,
...args: GetAsyncEventEmitterEventParameters<AsyncEventEmitter<Events>, K>
): boolean;

public emit(eventName: string | symbol, ...args: any[]): boolean;
public emit<K extends string | symbol>(
eventName: K,
...args: GetAsyncEventEmitterEventParameters<AsyncEventEmitter<Events>, K>
): boolean;

public emit(eventName: string | symbol, ...args: any[]): boolean {
let doError = eventName === 'error';
Expand Down Expand Up @@ -503,9 +546,15 @@ export class AsyncEventEmitter<
listener: AsyncEventEmitterListenerForEvent<AsyncEventEmitter<Events>, K>,
): this;

public prependListener(eventName: string | symbol, listener: (...args: any[]) => void): this;
public prependListener<K extends string | symbol>(
eventName: K,
listener: AsyncEventEmitterListenerForEvent<AsyncEventEmitter<Events>, K>,
): this;

public prependListener(eventName: string | symbol, listener: (...args: any[]) => void): this {
public prependListener<K extends string | symbol>(
eventName: K,
listener: AsyncEventEmitterListenerForEvent<AsyncEventEmitter<Events>, K>,
): this {
validateListener(listener);

const wrapped = this._wrapListener(eventName, listener, false);
Expand All @@ -520,9 +569,15 @@ export class AsyncEventEmitter<
listener: AsyncEventEmitterListenerForEvent<AsyncEventEmitter<Events>, K>,
): this;

public prependOnceListener(eventName: string | symbol, listener: (...args: any[]) => void): this;
public prependOnceListener<K extends string | symbol>(
eventName: K,
listener: AsyncEventEmitterListenerForEvent<AsyncEventEmitter<Events>, K>,
): this;

public prependOnceListener(eventName: string | symbol, listener: (...args: any[]) => void): this {
public prependOnceListener<K extends string | symbol>(
eventName: K,
listener: AsyncEventEmitterListenerForEvent<AsyncEventEmitter<Events>, K>,
): this {
validateListener(listener);

const wrapped = this._wrapListener(eventName, listener, true);
Expand Down Expand Up @@ -639,26 +694,12 @@ export class AsyncEventEmitter<
options?: AbortableMethods,
): Promise<GetAsyncEventEmitterEventParameters<Emitter, EventName>>;

public static async once<
Emitter extends AsyncEventEmitter<any>,
EventNames extends Record<PropertyKey, unknown[]> = Emitter extends AsyncEventEmitter<infer Events> ? Events
: Record<PropertyKey, unknown[]>,
EventName extends PropertyKey = keyof EventNames | keyof AsyncEventEmitterPredefinedEvents,
EventResult extends unknown[] = GetAsyncEventEmitterEventParameters<Emitter, EventName>,
>(emitter: Emitter, eventName: EventName, options: AbortableMethods): Promise<EventResult>;

public static async once(
emitter: AsyncEventEmitter<any>,
eventName: string | symbol,
options?: AbortableMethods,
): Promise<any[]>;

public static async once(
emitter: AsyncEventEmitter<any>,
eventName: string | symbol,
options: AbortableMethods,
): Promise<any[]>;

public static async once(
emitter: AsyncEventEmitter<any>,
eventName: string | symbol,
Expand Down Expand Up @@ -720,29 +761,12 @@ export class AsyncEventEmitter<
options?: AbortableMethods,
): AsyncGenerator<GetAsyncEventEmitterEventParameters<Emitter, EventName>, void>;

public static on<
Emitter extends AsyncEventEmitter<any>,
EventNames extends Record<PropertyKey, unknown[]> = Emitter extends AsyncEventEmitter<infer Events> ? Events
: Record<PropertyKey, unknown[]>,
EventName extends PropertyKey = keyof EventNames | keyof AsyncEventEmitterPredefinedEvents,
>(
emitter: Emitter,
eventName: EventName,
options: AbortableMethods,
): AsyncGenerator<GetAsyncEventEmitterEventParameters<Emitter, EventName>, void>;

public static on(
emitter: AsyncEventEmitter<any>,
eventName: string | symbol,
options?: AbortableMethods,
): AsyncGenerator<any[], void>;

public static on(
emitter: AsyncEventEmitter<any>,
eventName: string | symbol,
options: AbortableMethods,
): AsyncGenerator<any[], void>;

public static on(
emitter: AsyncEventEmitter<any>,
eventName: string | symbol,
Expand Down
34 changes: 33 additions & 1 deletion tests/types.ts
Original file line number Diff line number Diff line change
@@ -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];
Expand Down Expand Up @@ -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<typeof t, 'foo'> = (bar) => {
console.log(bar);
};

const listener2: AsyncEventEmitterListenerForEvent<typeof t, 'newListener'> = (...args) => {
console.log(args);
};

t.removeListener('foo', listener);

t.removeListener('foo', listener2);
}

{
const eventCount = AsyncEventEmitter.listenerCount(t, 'baz');

Expand All @@ -74,13 +100,17 @@ 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');

void AsyncEventEmitter.once(t, 'removeListener');

void AsyncEventEmitter.once(t, 'foo');

void AsyncEventEmitter.once(t, 'no dice');
}

{
Expand Down Expand Up @@ -110,6 +140,8 @@ declare const ee: AsyncEventEmitter;

const events = ee.eventNames();
// ^?

events.includes('owo');
}

interface Events {
Expand Down

0 comments on commit 75c7e19

Please sign in to comment.