event-target-shim
Advanced tools
Comparing version 4.0.3 to 5.0.0-beta.0
358
index.d.ts
@@ -1,3 +0,7 @@ | ||
export as namespace EventTargetShim; | ||
export as namespace EventTargetShim | ||
/** | ||
* `Event` interface. | ||
* @see https://dom.spec.whatwg.org/#event | ||
*/ | ||
export interface Event { | ||
@@ -7,3 +11,3 @@ /** | ||
*/ | ||
readonly type: string; | ||
readonly type: string | ||
@@ -13,3 +17,3 @@ /** | ||
*/ | ||
readonly target: EventTarget | null; | ||
readonly target: EventTarget<{}, {}, "standard"> | null | ||
@@ -19,8 +23,9 @@ /** | ||
*/ | ||
readonly currentTarget: EventTarget | null; | ||
readonly currentTarget: EventTarget<{}, {}, "standard"> | null | ||
/** | ||
* The target of this event. | ||
* @deprecated | ||
*/ | ||
readonly srcElement: any | null; | ||
readonly srcElement: any | null | ||
@@ -30,3 +35,3 @@ /** | ||
*/ | ||
composedPath(): EventTarget[]; | ||
composedPath(): EventTarget<{}, {}, "standard">[] | ||
@@ -36,3 +41,3 @@ /** | ||
*/ | ||
readonly NONE: number; | ||
readonly NONE: number | ||
@@ -42,3 +47,3 @@ /** | ||
*/ | ||
readonly CAPTURING_PHASE: number; | ||
readonly CAPTURING_PHASE: number | ||
@@ -48,3 +53,3 @@ /** | ||
*/ | ||
readonly BUBBLING_PHASE: number; | ||
readonly BUBBLING_PHASE: number | ||
@@ -54,3 +59,3 @@ /** | ||
*/ | ||
readonly AT_TARGET: number; | ||
readonly AT_TARGET: number | ||
@@ -60,3 +65,3 @@ /** | ||
*/ | ||
readonly eventPhase: number; | ||
readonly eventPhase: number | ||
@@ -66,3 +71,3 @@ /** | ||
*/ | ||
stopPropagation(): void; | ||
stopPropagation(): void | ||
@@ -72,3 +77,3 @@ /** | ||
*/ | ||
stopImmediatePropagation(): void; | ||
stopImmediatePropagation(): void | ||
@@ -79,3 +84,3 @@ /** | ||
*/ | ||
initEvent(type: string, bubbles?: boolean, cancelable?: boolean): void; | ||
initEvent(type: string, bubbles?: boolean, cancelable?: boolean): void | ||
@@ -85,13 +90,15 @@ /** | ||
*/ | ||
readonly bubbles: boolean; | ||
readonly bubbles: boolean | ||
/** | ||
* Stop event bubbling. | ||
* @deprecated | ||
*/ | ||
cancelBubble: boolean; | ||
cancelBubble: boolean | ||
/** | ||
* Set or get cancellation flag. | ||
* @deprecated | ||
*/ | ||
returnValue: boolean; | ||
returnValue: boolean | ||
@@ -101,3 +108,3 @@ /** | ||
*/ | ||
readonly cancelable: boolean; | ||
readonly cancelable: boolean | ||
@@ -107,3 +114,3 @@ /** | ||
*/ | ||
preventDefault(): void; | ||
preventDefault(): void | ||
@@ -113,3 +120,3 @@ /** | ||
*/ | ||
readonly defaultPrevented: boolean; | ||
readonly defaultPrevented: boolean | ||
@@ -119,3 +126,3 @@ /** | ||
*/ | ||
readonly composed: boolean; | ||
readonly composed: boolean | ||
@@ -125,3 +132,3 @@ /** | ||
*/ | ||
readonly isTrusted: boolean; | ||
readonly isTrusted: boolean | ||
@@ -131,22 +138,26 @@ /** | ||
*/ | ||
readonly timeStamp: number; | ||
readonly timeStamp: number | ||
} | ||
export interface EventListenerOptions { | ||
capture?: boolean; | ||
/** | ||
* The constructor of `EventTarget` interface. | ||
*/ | ||
export type EventTargetConstructor< | ||
TEvents extends EventTarget.EventDefinition = {}, | ||
TEventAttributes extends EventTarget.EventDefinition = {}, | ||
TMode extends EventTarget.Mode = "loose" | ||
> = { | ||
prototype: EventTarget<TEvents, TEventAttributes, TMode> | ||
new(): EventTarget<TEvents, TEventAttributes, TMode> | ||
} | ||
export interface AddEventListenerOptions extends EventListenerOptions { | ||
passive?: boolean; | ||
once?: boolean; | ||
} | ||
export type EventTargetListener = ((event: Event) => void) | { handleEvent(event: Event): void }; | ||
export interface PartialEvent extends Partial<Event> { | ||
type: string; | ||
[key: string]: any; | ||
} | ||
export interface EventTarget { | ||
/** | ||
* `EventTarget` interface. | ||
* @see https://dom.spec.whatwg.org/#interface-eventtarget | ||
*/ | ||
export type EventTarget< | ||
TEvents extends EventTarget.EventDefinition = {}, | ||
TEventAttributes extends EventTarget.EventDefinition = {}, | ||
TMode extends EventTarget.Mode = "loose" | ||
> = EventTarget.EventAttributes<TEventAttributes> & { | ||
/** | ||
@@ -158,7 +169,9 @@ * Add a given listener to this event target. | ||
*/ | ||
addEventListener( | ||
eventName: string, | ||
listener: EventTargetListener | null, | ||
options?: boolean | AddEventListenerOptions, | ||
): void; | ||
addEventListener<TEventType extends EventTarget.EventType<TEvents, TMode>>( | ||
type: TEventType, | ||
listener: | ||
| EventTarget.Listener<EventTarget.PickEvent<TEvents, TEventType>> | ||
| null, | ||
options?: boolean | EventTarget.AddOptions | ||
): void | ||
@@ -171,7 +184,9 @@ /** | ||
*/ | ||
removeEventListener( | ||
eventName: string, | ||
listener: EventTargetListener | null, | ||
options?: boolean | EventListenerOptions, | ||
): void; | ||
removeEventListener<TEventType extends EventTarget.EventType<TEvents, TMode>>( | ||
type: TEventType, | ||
listener: | ||
| EventTarget.Listener<EventTarget.PickEvent<TEvents, TEventType>> | ||
| null, | ||
options?: boolean | EventTarget.RemoveOptions | ||
): void | ||
@@ -183,40 +198,235 @@ /** | ||
*/ | ||
dispatchEvent(event: PartialEvent): boolean; | ||
dispatchEvent<TEventType extends EventTarget.EventType<TEvents, TMode>>( | ||
event: EventTarget.EventData<TEvents, TEventType, TMode> | ||
): boolean | ||
} | ||
type EventAttributes<T extends string> = { | ||
[K in T]: ((ev: Event) => void) | null; | ||
}; | ||
export const EventTarget: EventTargetConstructor & { | ||
/** | ||
* Create an `EventTarget` instance with detailed event definition. | ||
* | ||
* The detailed event definition requires to use `defineEventAttribute()` | ||
* function later. | ||
* | ||
* Unfortunately, the second type parameter `TEventAttributes` was needed | ||
* because we cannot compute string literal types. | ||
* | ||
* @example | ||
* const signal = new EventTarget<{ abort: Event }, { onabort: Event }>() | ||
* defineEventAttribute(signal, "abort") | ||
*/ | ||
new < | ||
TEvents extends EventTarget.EventDefinition, | ||
TEventAttributes extends EventTarget.EventDefinition, | ||
TMode extends EventTarget.Mode = "loose" | ||
>(): EventTarget<TEvents, TEventAttributes, TMode> | ||
type EventTargetConstructor = { | ||
prototype: EventTarget; | ||
new(): EventTarget; | ||
}; | ||
/** | ||
* Define an `EventTarget` constructor with attribute events. | ||
* @param events Event types to define attribute events (e.g. passing in `"click"` adds `onclick` to prototype). | ||
*/ | ||
(events: string[]): EventTargetConstructor | ||
/** | ||
* Define an `EventTarget` constructor with attribute events. | ||
* @param events Event types to define attribute events (e.g. passing in `"click"` adds `onclick` to prototype). | ||
*/ | ||
(...events: string[]): EventTargetConstructor | ||
type ExEventTarget<T extends string> = EventTarget & EventAttributes<T>; | ||
/** | ||
* Define an `EventTarget` constructor with attribute events and detailed event definition. | ||
* | ||
* Unfortunately, the second type parameter `TEventAttributes` was needed | ||
* because we cannot compute string literal types. | ||
* | ||
* @example | ||
* class AbortSignal extends EventTarget<{ abort: Event }, { onabort: Event }>("abort") { | ||
* abort(): void {} | ||
* } | ||
* | ||
* @param events Optional event attributes (e.g. passing in `"click"` adds `onclick` to prototype). | ||
*/ | ||
< | ||
TEvents extends EventTarget.EventDefinition, | ||
TEventAttributes extends EventTarget.EventDefinition, | ||
TMode extends EventTarget.Mode = "loose" | ||
>(events: (keyof TEvents)[]): EventTargetConstructor< | ||
TEvents, | ||
TEventAttributes, | ||
TMode | ||
> | ||
type ExEventTargetConstructor<T extends string> = { | ||
prototype: ExEventTarget<T>; | ||
new(): ExEventTarget<T>; | ||
}; | ||
export const EventTarget: { | ||
new(): EventTarget; | ||
new<T extends string>(): ExEventTarget<T>; | ||
/** | ||
* The event target wrapper to be used when extending objects. | ||
* Define an `EventTarget` constructor with attribute events and detailed event definition. | ||
* | ||
* Unfortunately, the second type parameter `TEventAttributes` was needed | ||
* because we cannot compute string literal types. | ||
* | ||
* @example | ||
* class AbortSignal extends EventTarget<{ abort: Event }, { onabort: Event }>("abort") { | ||
* abort(): void {} | ||
* } | ||
* | ||
* @param events Optional event attributes (e.g. passing in `"click"` adds `onclick` to prototype). | ||
*/ | ||
(events: string[]): EventTargetConstructor; | ||
(...events: string[]): EventTargetConstructor; | ||
<T extends string>(events: string[]): ExEventTargetConstructor<T>; | ||
<T extends string>(...events: string[]): ExEventTargetConstructor<T>; | ||
}; | ||
export default EventTarget; | ||
< | ||
TEvents extends EventTarget.EventDefinition, | ||
TEventAttributes extends EventTarget.EventDefinition, | ||
TMode extends EventTarget.Mode = "loose" | ||
>(...events: (keyof TEvents)[]): EventTargetConstructor< | ||
TEvents, | ||
TEventAttributes, | ||
TMode | ||
> | ||
} | ||
export namespace EventTarget { | ||
/** | ||
* Options of `removeEventListener()` method. | ||
*/ | ||
export interface RemoveOptions { | ||
/** | ||
* The flag to indicate that the listener is for the capturing phase. | ||
*/ | ||
capture?: boolean | ||
} | ||
/** | ||
* Options of `addEventListener()` method. | ||
*/ | ||
export interface AddOptions extends RemoveOptions { | ||
/** | ||
* The flag to indicate that the listener doesn't support | ||
* `event.preventDefault()` operation. | ||
*/ | ||
passive?: boolean | ||
/** | ||
* The flag to indicate that the listener will be removed on the first | ||
* event. | ||
*/ | ||
once?: boolean | ||
} | ||
/** | ||
* The type of regular listeners. | ||
*/ | ||
export interface FunctionListener<TEvent> { | ||
(event: TEvent): void | ||
} | ||
/** | ||
* The type of object listeners. | ||
*/ | ||
export interface ObjectListener<TEvent> { | ||
handleEvent(event: TEvent): void | ||
} | ||
/** | ||
* The type of listeners. | ||
*/ | ||
export type Listener<TEvent = Event> = | ||
| FunctionListener<TEvent> | ||
| ObjectListener<TEvent> | ||
/** | ||
* Event definition. | ||
*/ | ||
export type EventDefinition = { | ||
readonly [key: string]: Event | ||
} | ||
/** | ||
* Mapped type for event attributes. | ||
*/ | ||
export type EventAttributes<TEventAttributes extends EventDefinition> = { | ||
[P in keyof TEventAttributes]: | ||
| FunctionListener<TEventAttributes[P]> | ||
| null | ||
} | ||
/** | ||
* The type of event data for `dispatchEvent()` method. | ||
*/ | ||
export type EventData< | ||
TEvents extends EventDefinition, | ||
TEventType extends keyof TEvents | string, | ||
TMode extends Mode | ||
> = | ||
TEventType extends keyof TEvents | ||
? ( | ||
// Require properties which are not generated automatically. | ||
& Pick< | ||
TEvents[TEventType], | ||
Exclude<keyof TEvents[TEventType], OmittableEventKeys> | ||
> | ||
// Properties which are generated automatically are optional. | ||
& Partial<Pick<Event, OmittableEventKeys>> | ||
) | ||
: ( | ||
TMode extends "standard" | ||
? Event | ||
: Event | NonStandardEvent | ||
) | ||
/** | ||
* The string literal types of the properties which are generated | ||
* automatically in `dispatchEvent()` method. | ||
*/ | ||
export type OmittableEventKeys = Exclude<keyof Event, "type"> | ||
/** | ||
* The type of event data. | ||
*/ | ||
export type NonStandardEvent = { | ||
[key: string]: any | ||
type: string | ||
} | ||
/** | ||
* The type of listeners. | ||
*/ | ||
export type PickEvent< | ||
TEvents extends EventDefinition, | ||
TEventType extends keyof TEvents | string, | ||
> = | ||
TEventType extends keyof TEvents | ||
? TEvents[TEventType] | ||
: Event | ||
/** | ||
* Event type candidates. | ||
*/ | ||
export type EventType< | ||
TEvents extends EventDefinition, | ||
TMode extends Mode | ||
> = | ||
TMode extends "strict" | ||
? keyof TEvents | ||
: keyof TEvents | string | ||
/** | ||
* - `"strict"` ..... Methods don't accept unknown events. | ||
* `dispatchEvent()` accepts partial objects. | ||
* - `"loose"` ...... Methods accept unknown events. | ||
* `dispatchEvent()` accepts partial objects. | ||
* - `"standard"` ... Methods accept unknown events. | ||
* `dispatchEvent()` doesn't accept partial objects. | ||
*/ | ||
export type Mode = "strict" | "standard" | "loose" | ||
} | ||
/** | ||
* Specialized `type` property. | ||
*/ | ||
export type Type<T extends string> = { type: T } | ||
/** | ||
* Define an event attribute (e.g. `eventTarget.onclick`). | ||
* @param eventTargetPrototype The event target prototype to define an event attribute. | ||
* @param prototype The event target prototype to define an event attribute. | ||
* @param eventName The event name to define. | ||
*/ | ||
export function defineEventAttribute(eventTargetPrototype: EventTarget, eventName: string): void; | ||
export function defineEventAttribute( | ||
prototype: EventTarget, | ||
eventName: string | ||
): void | ||
export default EventTarget |
{ | ||
"name": "event-target-shim", | ||
"version": "4.0.3", | ||
"version": "5.0.0-beta.0", | ||
"description": "An implementation of WHATWG EventTarget interface.", | ||
@@ -24,7 +24,6 @@ "main": "dist/event-target-shim", | ||
"test": "run-s test:*", | ||
"test:mocha": "nyc --require ./scripts/babel-register mocha test/index.mjs", | ||
"test:mocha": "nyc --require ./scripts/babel-register mocha test/*.mjs", | ||
"test:karma": "karma start scripts/karma.conf.js --single-run", | ||
"test:types": "tsc", | ||
"watch": "run-p watch:*", | ||
"watch:mocha": "mocha test/index.mjs --require ./scripts/babel-register --watch --watch-extensions js,mjs --growl", | ||
"watch:mocha": "mocha test/*.mjs --require ./scripts/babel-register --watch --watch-extensions js,mjs --growl", | ||
"watch:karma": "karma start scripts/karma.conf.js --watch", | ||
@@ -31,0 +30,0 @@ "codecov": "codecov" |
@@ -154,14 +154,18 @@ # event-target-shim | ||
Currently typescript does not support type mutation by method, therefore the previous example will **not work** without the following modifications: | ||
#### Working example #1 | ||
```ts | ||
import { EventTarget, defineEventAttribute } from "event-target-shim"; | ||
// Define events | ||
type FooEvents = { | ||
hello: CustomEvent | ||
} | ||
type FooEventAttributes = { | ||
onhello: CustomEvent | ||
} | ||
// Define a derived class. | ||
class Foo extends EventTarget<"onhello"> { | ||
class Foo extends EventTarget<FooEvents, FooEventAttributes> { | ||
// ... | ||
} | ||
// Define `foo.onhello` property. | ||
// Define `foo.onhello` property's implementation. | ||
defineEventAttribute(Foo.prototype, "hello") | ||
@@ -172,6 +176,6 @@ | ||
foo.addEventListener("hello", (e) => { | ||
console.log("hello", e) | ||
console.log("hello", e.detail) | ||
}) | ||
foo.onhello = (e) => { | ||
console.log("onhello", e) | ||
console.log("onhello", e.detail) | ||
} | ||
@@ -183,24 +187,46 @@ | ||
In the future, if typescript adds support to string literal mutation (joining, slicing and etc.), it should be possible to automatically infer `onhello` type from `hello`. However, until then you are stuck with this: | ||
Unfortunately, both `FooEvents` and `FooEventAttributes` are needed because TypeScript doesn't allow the mutation of string literal types. If TypeScript allowed us to compute `"onhello"` from `"hello"` in types, `FooEventAttributes` will be optional. | ||
#### Working example #2 | ||
This `EventTarget` type is compatible with `EventTarget` interface of `lib.dom.d.ts`. | ||
#### To disallow unknown events | ||
By default, methods such as `addEventListener` accept unknown events. You can disallow unknown events by the third type parameter `"strict"`. | ||
```ts | ||
import { EventTarget, defineEventAttribute } from "event-target-shim"; | ||
// Define a derived class. | ||
class Foo extends EventTarget<"onhello">("hello") { | ||
type FooEvents = { | ||
hello: CustomEvent | ||
} | ||
class Foo extends EventTarget<FooEvents, {}, "strict"> { | ||
// ... | ||
} | ||
// Register event listeners. | ||
const foo = new Foo() | ||
// OK because `hello` is defined in FooEvents. | ||
foo.addEventListener("hello", (e) => { | ||
console.log("hello", e) | ||
}) | ||
foo.onhello = (e) => { | ||
console.log("onhello", e) | ||
// Error because `unknown` is not defined in FooEvents. | ||
foo.addEventListener("unknown", (e) => { | ||
}) | ||
``` | ||
However, if you use `"strict"` parameter, it loses compatibility with `EventTarget` interface of `lib.dom.d.ts`. | ||
#### To infer the type of `dispatchEvent()` method | ||
TypeScript cannot infer the event type of `dispatchEvent()` method properly from the argument in most cases. You can improve this behavior with the following steps: | ||
1. Use the third type parameter `"strict"`. This prevents inferring to `dispatchEvent<string>()`. | ||
2. Make the `type` property of event definitions stricter. | ||
```ts | ||
type FooEvents = { | ||
hello: CustomEvent & { type: "hello" } | ||
hey: Event & { type: "hey" } | ||
} | ||
class Foo extends EventTarget<FooEvents, {}, "strict"> { | ||
// ... | ||
} | ||
// Dispatching events | ||
foo.dispatchEvent(new CustomEvent("hello", { detail: "detail" })) | ||
// Error because `detail` property is lacking. | ||
foo.dispatchEvent({ type: "hello" }) | ||
``` | ||
@@ -207,0 +233,0 @@ |
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
189344
1955
294
1