microsoft / microsoft/TypeScript

Associate HTML Elements and EventMaps in a map

Abierto
#40,689 4 comentarios 18 reacciones 0 asignados Ver en GitHub

Nadie ha tomado este issue todavía.

Awaiting More Feedback Suggestion
Lenguaje dominante
Go
Estrellas
111k
Forks
14.3k
Merge medio
1 d 19 h
PR fusionados (30 d)
117

Descripción

Search Terms

  • dom.d.ts
  • eventmap

Suggestion

Create a "map" that pairs HTMLElement with it's relevant EventMap types.

We can infer a tagName from a HTMLElement and visa-versa, from the HTMLElementTagNameMap type.


type GetHTMLElementByTagName<
  K extends keyof HTMLElementTagNameMap
> = HTMLElementTagNameMap[K];

type GetTagNameByHTMLElement<
  E extends HTMLElementTagNameMap[keyof HTMLElementTagNameMap]
> = {
  [P in keyof HTMLElementTagNameMap]: HTMLElementTagNameMap[P] extends E
    ? P
    : never;
}[keyof HTMLElementTagNameMap];


But there is currently no way with Typescript's type system to infer what the event map of an element is from the existing types in dom.d.ts.

interface HTMLElement  extends ... {

// ...
// how to extract type HTMLElementEventMap from keyof HTMLElementEventMap?
  addEventListener<K extends keyof HTMLElementEventMap>(type: K, listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => any, options?: boolean | AddEventListenerOptions): void;
// ...

Use Cases

It would be nice to create our own versions of [element].(add|remove)EventListener(). For the drag and drop library sortablejs, we expose a helper that is [element].(add|remove)EventListener() that assigns the capture value based on the browser.

function off(el, event, fn) {
  el.removeEventListener(event, fn, captureMode());
}

Unfortunately, I cannot infer the event and fn function parameters from the element el with generics.

Examples

All these other "map" implementations are string <-> Type pairs using an interface, where as we'll need Type <-> Type pairs.

// dom.d.ts
interface HTMLElementTagNameMap {
    "a": HTMLAnchorElement;
    "abbr": HTMLElement;
    "address": HTMLElement;
    "applet": HTMLAppletElement;
    // ...
}

Here is the janky work around I've come up with so far. You can see more in dom-ts. I figured many other users would want this, which is why I've created this issue.

// helper
export type CreateElementMeta<R extends string, E extends Element, A extends ElementEventMap> = {
  _tagName: R
  _element: E
  _eventMap: A
}

// creating every type.. this is an example of one.
export type HTMLFormElementMeta = CreateElementMeta<
  "form",
  HTMLFormElement,
  HTMLFrameSetElementEventMap
>

// Union of ALL these meta types
type AllElementMeta = HTMLFormElementMeta | HTML[Name]Element //...

// helper for matching types, in this case
//  filtering out mismatched tagNames

export type MatchTagName<R extends string> = AllElementMeta extends infer T
  ? T extends CreateElementMeta<R, infer E, infer A>
    ? CreateElementMeta<R, E, A>
    : never
  : never


// using said types on document.creatElement
declare function createElement<
  K extends meta.AllElementMeta["_tagName"],
  L extends meta.AllElementMeta["_tagName"]
>(
  tagName: K,
  options: { is: L }
): meta.MatchTagName<K>["_element"] & meta.MatchTagName<L>["_element"]

Checklist

My suggestion meets these guidelines:

  • 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, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.

Side Notes

  • This is a breaking change, but unsure how it could be handled elegantly since Typescript doesn't follow the semantic versioning specification.
  • There are some missing events like pointermove on HTMLElement. These should be updated. I'll post a new issue unless advised otherwise.
  • Users using custom elements and custom events need a way to insert their own Element <-> EventMap pairs. This would probably be done by append their types to the global namespace.

Guía de contribución

Abrir la guía de contribución

Primeros pasos

  1. Lee el issue completo y luego la guía de contribución del proyecto.
  2. Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
  3. Haz un fork del repositorio y trabaja en una rama.
  4. Abre un pull request que haga referencia al número del issue.

Línea de trabajo

Comienza leyendo las declaraciones existentes de HTMLElementTagNameMap y addEventListener en dom.d.ts; después, revisa los ejemplos de ElementEventMap y el workaround de dom-ts enlazado. Determina si una asociación a nivel de tipos entre HTMLElement y EventMap puede admitir el caso de uso indicado de add/removeEventListener, incluidos los elementos personalizados, y valida el diseño con las definiciones de tipos DOM existentes.

Escrito por el modelo de indexación a partir del texto del issue.

Evaluación

Stack tecnológico
typescript
Área
frontend
Tipo de issue
Nueva funcionalidad
Dificultad
5/5
Tiempo estimado
Más de una semana
Estado de actividad
Estancado
Claridad
Bastante claro
Aptitud para principiantes
28/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.