Skip to content

Some ideas and possible issue with enforcing correctly dispatched string key #1

@Glidias

Description

@Glidias

You can create 2 versions of these, one with arbituary string keys (scroll below to bottom, just enforce a singly typed event) and the other requiring specific string keys (the current version you are using).

Also, for the one with specific string keys, is there a way to use regular dispatchEvent instead of deprecrating it in favour of dispatchedTypeEvent?? I guess that is not possible because the native JAvascript Event enforces arbituary string types internally hmmm...

Possible issue, regarding https://github.com/DerZade/typescript-event-target#dispatching-events, you can't enforce specific string keys for events that are being dispatched though...

const eventTarget = new TypedKeyEventTarget<{"time":MouseEvent}>();
eventTarget.dispatchTypedEvent(
    'time',
    new MouseEvent('doesnt match above if mispelled will not work')
);

Typically, most enforced string keys are found as static constants within the class itself as a lookup to avoid typos, but compiler wise it wil still accept rather than reject.


// Typed KeyEventTarget version

export type TypedEventListener<M, T extends keyof M> = (
    evt: M[T]
) => void | Promise<void>;

export interface TypedEventListenerObject<M, T extends keyof M> {
    handleEvent: (evt: M[T]) => void | Promise<void>;
}

export type TypedEventListenerOrEventListenerObject<M, T extends keyof M> =
    | TypedEventListener<M, T>
    | TypedEventListenerObject<M, T>;

type ValueIsEvent<T> = {
    [key in keyof T]: Event;
};


export interface TypedKeyEventTarget<M extends ValueIsEvent<M>> {
    /** Appends an event listener for events whose type attribute value is type.
     * The callback argument sets the callback that will be invoked when the event
     * is dispatched.
     *
     * The options argument sets listener-specific options. For compatibility this
     * can be a boolean, in which case the method behaves exactly as if the value
     * was specified as options's capture.
     *
     * When set to true, options's capture prevents callback from being invoked
     * when the event's eventPhase attribute value is BUBBLING_PHASE. When false
     * (or not present), callback will not be invoked when event's eventPhase
     * attribute value is CAPTURING_PHASE. Either way, callback will be invoked if
     * event's eventPhase attribute value is AT_TARGET.
     *
     * When set to true, options's passive indicates that the callback will not
     * cancel the event by invoking preventDefault(). This is used to enable
     * performance optimizations described in § 2.8 Observing event listeners.
     *
     * When set to true, options's once indicates that the callback will only be
     * invoked once after which the event listener will be removed.
     *
     * The event listener is appended to target's event listener list and is not
     * appended if it has the same type, callback, and capture. */
    addEventListener: <T extends keyof M & string>(
        type: string,
        listener: TypedEventListenerOrEventListenerObject<M, T> | null,
        options?: boolean | AddEventListenerOptions
    ) => void;

    /** Removes the event listener in target's event listener list with the same
     * type, callback, and options. */
    removeEventListener: <T extends keyof M & string>(
        type: T,
        callback: TypedEventListenerOrEventListenerObject<M, T> | null,
        options?: EventListenerOptions | boolean
    ) => void;

    /**
     * Dispatches a synthetic event event to target and returns true if either
     * event's cancelable attribute value is false or its preventDefault() method
     * was not invoked, and false otherwise.
     * @deprecated To ensure type safety use `dispatchTypedEvent` instead.
     */
    dispatchEvent: (event: Event) => boolean;
}

export class TypedKeyEventTarget<M extends ValueIsEvent<M>> extends EventTarget {
    /**
     * Dispatches a synthetic event event to target and returns true if either
     * event's cancelable attribute value is false or its preventDefault() method
     * was not invoked, and false otherwise.
     */
    public dispatchTypedEvent<T extends keyof M>(
        _type: T,
        event: M[T]
    ): boolean {
        return super.dispatchEvent(event);
    }
}

// Typed event version (Partial M) (with arbituary string keys)

interface MEventListener<M extends Event> {
    (evt: M): void;
}

interface MEventListenerObject<M extends Event> {
    handleEvent(object: M): void;
}

export interface TypedEventTarget<_M extends Event> {
    /** Appends an event listener for events whose type attribute value is type.
     * The callback argument sets the callback that will be invoked when the event
     * is dispatched.
     *
     * The options argument sets listener-specific options. For compatibility this
     * can be a boolean, in which case the method behaves exactly as if the value
     * was specified as options's capture.
     *
     * When set to true, options's capture prevents callback from being invoked
     * when the event's eventPhase attribute value is BUBBLING_PHASE. When false
     * (or not present), callback will not be invoked when event's eventPhase
     * attribute value is CAPTURING_PHASE. Either way, callback will be invoked if
     * event's eventPhase attribute value is AT_TARGET.
     *
     * When set to true, options's passive indicates that the callback will not
     * cancel the event by invoking preventDefault(). This is used to enable
     * performance optimizations described in § 2.8 Observing event listeners.
     *
     * When set to true, options's once indicates that the callback will only be
     * invoked once after which the event listener will be removed.
     *
     * The event listener is appended to target's event listener list and is not
     * appended if it has the same type, callback, and capture. */
    addEventListener: (
        type: string,
        listener: MEventListener<_M> | MEventListenerObject<_M> | null,
        options?: boolean | AddEventListenerOptions
    ) => void;

    /** Removes the event listener in target's event listener list with the same
     * type, callback, and options. */
    removeEventListener: (
        type: string,
        callback: MEventListener<_M> | MEventListenerObject<_M> | null,
        options?: EventListenerOptions | boolean
    ) => void;

    /**
     * Dispatches a synthetic event event to target and returns true if either
     * event's cancelable attribute value is false or its preventDefault() method
     * was not invoked, and false otherwise.
     */
    dispatchEvent: (event: _M) => boolean
}

export class TypedEventTarget<_M> extends EventTarget {}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions