| 
 | 1 | +/**  | 
 | 2 | + * Copyright (c) 2022 Jonas "DerZade" Schade  | 
 | 3 | + *  | 
 | 4 | + * SPDX-License-Identifier: MIT  | 
 | 5 | + *  | 
 | 6 | + * https://github.com/DerZade/typescript-event-target/blob/master/src/TypedEventTarget.ts  | 
 | 7 | + */  | 
 | 8 | + | 
 | 9 | +/**  | 
 | 10 | + * A function that can be passed to the `listener` parameter of {@link TypedEventTarget.addEventListener} and {@link TypedEventTarget.removeEventListener}.  | 
 | 11 | + *  | 
 | 12 | + * @template M A map of event types to their respective event classes.  | 
 | 13 | + * @template T The type of event to listen for (has to be keyof `M`).  | 
 | 14 | + */  | 
 | 15 | +export type TypedEventListener<M, T extends keyof M> = (  | 
 | 16 | +  evt: M[T]  | 
 | 17 | +) => void | Promise<void>;  | 
 | 18 | + | 
 | 19 | +/**  | 
 | 20 | + * An object that can be passed to the `listener` parameter of {@link TypedEventTarget.addEventListener} and {@link TypedEventTarget.removeEventListener}.  | 
 | 21 | + *  | 
 | 22 | + * @template M A map of event types to their respective event classes.  | 
 | 23 | + * @template T The type of event to listen for (has to be keyof `M`).  | 
 | 24 | + */  | 
 | 25 | +export interface TypedEventListenerObject<M, T extends keyof M> {  | 
 | 26 | +  handleEvent: (evt: M[T]) => void | Promise<void>;  | 
 | 27 | +}  | 
 | 28 | + | 
 | 29 | +/**  | 
 | 30 | + * Type of parameter `listener` in {@link TypedEventTarget.addEventListener} and {@link TypedEventTarget.removeEventListener}.  | 
 | 31 | + *  | 
 | 32 | + * The object that receives a notification (an object that implements the Event interface) when an event of the specified type occurs.  | 
 | 33 | + *  | 
 | 34 | + * Can be either an object with a handleEvent() method, or a JavaScript function.  | 
 | 35 | + *  | 
 | 36 | + * @template M A map of event types to their respective event classes.  | 
 | 37 | + * @template T The type of event to listen for (has to be keyof `M`).  | 
 | 38 | + */  | 
 | 39 | +export type TypedEventListenerOrEventListenerObject<M, T extends keyof M> =  | 
 | 40 | +  | TypedEventListener<M, T>  | 
 | 41 | +  | TypedEventListenerObject<M, T>;  | 
 | 42 | + | 
 | 43 | +type ValueIsEvent<T> = {  | 
 | 44 | +  [key in keyof T]: Event;  | 
 | 45 | +};  | 
 | 46 | + | 
 | 47 | +/**  | 
 | 48 | + * Typescript friendly version of {@link EventTarget}  | 
 | 49 | + *  | 
 | 50 | + * @template M A map of event types to their respective event classes.  | 
 | 51 | + *  | 
 | 52 | + * @example  | 
 | 53 | + * ```typescript  | 
 | 54 | + * interface MyEventMap {  | 
 | 55 | + *     hello: Event;  | 
 | 56 | + *     time: CustomEvent<number>;  | 
 | 57 | + * }  | 
 | 58 | + *  | 
 | 59 | + * const eventTarget = new TypedEventTarget<MyEventMap>();  | 
 | 60 | + *  | 
 | 61 | + * eventTarget.addEventListener('time', (event) => {  | 
 | 62 | + *     // event is of type CustomEvent<number>  | 
 | 63 | + * });  | 
 | 64 | + * ```  | 
 | 65 | + */  | 
 | 66 | +export interface TypedEventTarget<M extends ValueIsEvent<M>> {  | 
 | 67 | +  /** Appends an event listener for events whose type attribute value is type.  | 
 | 68 | +   * The callback argument sets the callback that will be invoked when the event  | 
 | 69 | +   * is dispatched.  | 
 | 70 | +   *  | 
 | 71 | +   * The options argument sets listener-specific options. For compatibility this  | 
 | 72 | +   * can be a boolean, in which case the method behaves exactly as if the value  | 
 | 73 | +   * was specified as options's capture.  | 
 | 74 | +   *  | 
 | 75 | +   * When set to true, options's capture prevents callback from being invoked  | 
 | 76 | +   * when the event's eventPhase attribute value is BUBBLING_PHASE. When false  | 
 | 77 | +   * (or not present), callback will not be invoked when event's eventPhase  | 
 | 78 | +   * attribute value is CAPTURING_PHASE. Either way, callback will be invoked if  | 
 | 79 | +   * event's eventPhase attribute value is AT_TARGET.  | 
 | 80 | +   *  | 
 | 81 | +   * When set to true, options's passive indicates that the callback will not  | 
 | 82 | +   * cancel the event by invoking preventDefault(). This is used to enable  | 
 | 83 | +   * performance optimizations described in § 2.8 Observing event listeners.  | 
 | 84 | +   *  | 
 | 85 | +   * When set to true, options's once indicates that the callback will only be  | 
 | 86 | +   * invoked once after which the event listener will be removed.  | 
 | 87 | +   *  | 
 | 88 | +   * The event listener is appended to target's event listener list and is not  | 
 | 89 | +   * appended if it has the same type, callback, and capture. */  | 
 | 90 | +  addEventListener: <T extends keyof M & string>(  | 
 | 91 | +    type: T,  | 
 | 92 | +    listener: TypedEventListenerOrEventListenerObject<M, T> | null,  | 
 | 93 | +    options?: boolean | AddEventListenerOptions  | 
 | 94 | +  ) => void;  | 
 | 95 | + | 
 | 96 | +  /** Removes the event listener in target's event listener list with the same  | 
 | 97 | +   * type, callback, and options. */  | 
 | 98 | +  removeEventListener: <T extends keyof M & string>(  | 
 | 99 | +    type: T,  | 
 | 100 | +    callback: TypedEventListenerOrEventListenerObject<M, T> | null,  | 
 | 101 | +    options?: EventListenerOptions | boolean  | 
 | 102 | +  ) => void;  | 
 | 103 | + | 
 | 104 | +  /**  | 
 | 105 | +   * Dispatches a synthetic event event to target and returns true if either  | 
 | 106 | +   * event's cancelable attribute value is false or its preventDefault() method  | 
 | 107 | +   * was not invoked, and false otherwise.  | 
 | 108 | +   * @deprecated To ensure type safety use `dispatchTypedEvent` instead.  | 
 | 109 | +   */  | 
 | 110 | +  dispatchEvent: (event: Event) => boolean;  | 
 | 111 | +}  | 
 | 112 | +// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging  | 
 | 113 | +export class TypedEventTarget<M extends ValueIsEvent<M>> extends EventTarget {  | 
 | 114 | +  /**  | 
 | 115 | +   * Dispatches a synthetic event event to target and returns true if either  | 
 | 116 | +   * event's cancelable attribute value is false or its preventDefault() method  | 
 | 117 | +   * was not invoked, and false otherwise.  | 
 | 118 | +   */  | 
 | 119 | +  public dispatchTypedEvent<T extends keyof M>(_type: T, event: M[T]): boolean {  | 
 | 120 | +    return super.dispatchEvent(event);  | 
 | 121 | +  }  | 
 | 122 | +}  | 
0 commit comments