/**
 * Base type that a CustomEventEmitter may receive as a generic type
 * to discriminate possible events.
 */
type CustomEventMap = Record<string, unknown>;

/**
 * Extracts the keys from a CustomEventMap so they can be used as event types.
 * For example, for the CustomEventMap
 * ```ts
 * {
 *   "longpress": {position: number[]}
 *   "shortpress": {position: number[]}
 * }
 * ```
 * This type will infer `"longpress" | "shortpress"`.
 */
type CustomEventKey<EvtMap extends CustomEventMap> = string & keyof EvtMap;

/**
 * Any function or object that can be used to listen to an event.
 */
type CustomEventListenerOrEventListenerObject<T> =
  | { handleEvent(event: CustomEvent<T>): void }
  | ((event: CustomEvent<T>) => void);

/**
 * An event emitter that can be mixed in to other objects.
 */
interface CustomEventEmitter<EvtMap extends CustomEventMap> {
  addEventListener<K extends CustomEventKey<EvtMap>>(
    eventName: K,
    fn: CustomEventListenerOrEventListenerObject<EvtMap[K]>,
    options?: AddEventListenerOptions | boolean
  ): void;
  removeEventListener<K extends CustomEventKey<EvtMap>>(
    eventName: K,
    fn: CustomEventListenerOrEventListenerObject<EvtMap[K]>,
    options?: EventListenerOptions | boolean
  ): void;
  dispatchEvent<K extends CustomEventKey<EvtMap>>(
    eventName: K,
    detail: EvtMap[K]
  ): void;
  signal: AbortSignal;
  abort: AbortController["abort"];
}