microsoft / microsoft/TypeScript
Add a generic parameter to EventTarget for mapping events
Dieses Issue hat noch niemand übernommen.
- Vorherrschende Sprache
- Go
- Sterne
- 111k
- Forks
- 14.4k
- Ø Merge
- 1 T. 19 Std.
- Gemergte PRs (30 T.)
- 117
Beschreibung
Suggestion
🔍 Search Terms
EventTarget, addEventListener, removeEventListener
✅ Viability Checklist
- This wouldn't be a breaking change in existing TypeScript/JavaScript code
- This wouldn't change the runtime behavior of existing JavaScript code
- This could be implemented without emitting different JS based on the types of the expressions
- This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
- This feature would agree with the rest of TypeScript's Design Goals.
⭐ Suggestion
Currently we can extend EventTarget to hook any given class into the DOM events system:
class Foo extends EventTarget {}
const instance = new Foo();
instance.addEventListener('my-event', (ev) => { ... });
However, in the case of EventTarget, it has an addEventListener definition like so:
interface EventTarget {
addEventListener(
type: string,
listener: EventListenerOrEventListenerObject | null,
options?: boolean | AddEventListenerOptions
): void;
dispatchEvent(event: Event): boolean;
removeEventListener(
type: string,
callback: EventListenerOrEventListenerObject | null,
options?: EventListenerOptions | boolean
): void;
}
declare var EventTarget: {
prototype: EventTarget;
new(): EventTarget;
};
Due to this, it seems impossible to strongly type the events of our class.
Remember how things like window work:
addEventListener<K extends keyof WindowEventMap>(
type: K,
listener: (this: Window, ev: WindowEventMap[K]) => any,
options?: boolean | AddEventListenerOptions
): void;
This means we have strongly typed event names and types, giving us good intellisense when we do window.addEventListener.
So my suggestion is that we do similar for EventTarget:
interface EventTarget<EventMap> {
addEventListener<K extends keyof EventMap>(
type: K,
listener: (this: any, ev: EventMap[K]) => any, // `this` has to be `any` i suppose since we don't know what it is at this point
options?: boolean | AddEventListenerOptions
): void;
}
We'd still want an overload which consumes string but that would just fall back to Event.
Also, I'm unaware of any work around, so if there is already a known one, please do tell.
We also can't override this as a workaround, for example:
class Foo extends EventTarget {
addEventListener<K extends keyof FooMap>(
type: K,
listener: (ev: FooMap[K]) => void,
options?: boolean | AddEventListenerOptions
): void;
// ...[implementation and other overloads here]
}
as this would be like doing:
type Handler = (ev: Event) => void;
const myHandler: Handler = (ev: CustomEvent) => void; // error since it needs to handle ANY `Event`
You could possibly hack around it with interfaces:
interface FooEventTarget extends EventTarget {
addEventListener<K extends keyof FooMap>(
type: K,
listener: (ev: FooMap[K]) => void,
options?: boolean | AddEventListenerOptions
): void;
addEventListener(
type: string,
callback: EventListenerOrEventListenerObject | null,
options?: EventListenerOptions | boolean
): void;
}
const eventTarget = EventTarget as {new(): FooEventTarget; prototype: FooEventTarget};
class MyClass extends eventTarget {}
Beitragsleitfaden
Erste Schritte
- Lies das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreib ins Issue, dass du es übernimmst — das erspart doppelte Arbeit.
- Forke das Repository und arbeite in einem Branch.
- Öffne einen Pull Request, der die Issue-Nummer nennt.
Rechercherichtung
Beginne mit den im Issue gezeigten Deklarationen von EventTarget, addEventListener, removeEventListener und dispatchEvent und vergleiche sie anschließend mit der vorhandenen WindowEventMap-Überladung. Definiere, was die Unterstützung generischer Event-Maps und der Fallback von string zu Event beibehalten sollen; als abgeschlossen gilt die Aufgabe, wenn benutzerdefinierte Event-Namen und Payloads stark typisiert sind, ohne das Laufzeitverhalten von JavaScript zu ändern oder bestehende Aufrufe zu beeinträchtigen.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Bewertung
- Tech-Stack
- javascript, typescript
- Bereich
- frontend, web-dev
- Issue-Typ
- Feature
- Schwierigkeit
- 5/5
- Geschätzter Aufwand
- Über eine Woche
- Aktivitätsstatus
- Veraltet
- Klarheit
- Größtenteils klar
- Anfängerfreundlichkeit
- 35/100