TypeScript Associate HTML Elements and EventMaps in a map

3vpjnl9f  于 7个月前  发布在  TypeScript
关注(0)|答案(4)|浏览(87)

搜索词

  • dom.d.ts
  • eventmap

建议

创建一个"Map",将 HTMLElement 与其相关的 EventMap 类型配对。
我们可以从 HTMLElement 推导出一个 tagName ,反之亦然,从 HTMLElementTagNameMap 类型中可以推导出。

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];

但是,目前在 Typescript 的类型系统中,没有方法可以从 dom.d.ts 中的现有类型推断出元素的事件Map。

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;
// ...

用例

创建我们自己的 [element].(add|remove)EventListener() 版本会很好。对于拖放库 sortablejs ,我们暴露了一个基于浏览器分配捕获值的辅助函数 [element].(add|remove)EventListener()

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

不幸的是,我无法从具有泛型的元素 el 中推断出 eventfn 函数参数。

示例

所有其他 "Map" 实现都是使用接口的 string <-> Type 对,而我们需要的是 Type <-> Type 对。
以下是我目前想到的笨拙解决方法。我猜很多其他用户也会想要这个,这就是为什么我创建了这个问题。

检查清单

我的建议满足以下指导原则:

  • 这不会对现有的 TypeScript/JavaScript 代码造成破坏性更改
  • 这不会改变现有 JavaScript 代码的运行时行为
  • 这可以在不根据表达式的类型发出不同的 JS 的情况下实现
  • 这不是一个运行时特性(例如库功能、带有 JavaScript 输出的非 ECMAScript 语法等)
  • 这个特性将与 TypeScript's Design Goals 的其他部分保持一致。

边注

  • 这是个破坏性的更改,但不确定如何优雅地处理,因为 Typescript 没有遵循语义版本规范。
  • HTMLElement 上有一些缺失的事件,如 pointermove 。这些应该更新。除非得到建议,否则我会发布一个新的问题。
  • 使用自定义元素和自定义事件的用户需要一种插入他们自己的 Element <-> EventMap 对的方法。这可能通过将它们的类型附加到全局命名空间来完成。
i2byvkas

i2byvkas2#

@std4453 相关但无法从类型推断事件Map的问题。

uyhoqukh

uyhoqukh3#

How about this? (most of inspiration from @std4453's shared thing.)

// I only use the 3 types most of the time, so...
type DOMEventMapDefinitions = [
  [HTMLElement, HTMLElementEventMap],
  [Window, WindowEventMap],
  [Document, DocumentEventMap],
];

type DOMEventSubscriber = DOMEventMapDefinitions[number][0];
type MapDefinitionToEventMap<D extends { [K: number]: any[] }, T> = {
  [K in keyof D]: D[K] extends any[]
    ? T extends D[K][0]
      ? D[K][1]
      : never
    : never;
}[number];

type GetEventMapFromByElement<T extends DOMEventSubscriber> =
  MapDefinitionToEventMap<DOMEventMapDefinitions, T>;

// usage
function customAddEventListener<
  Element extends DOMEventSubscriber,
  Type extends keyof GetEventMapFromByElement<Element> & string,
  Event extends GetEventMapFromByElement<Element>[Type]
>(element: Element, eventType: Type, listener: (event: Event) => any, options?: boolean | AddEventListenerOptions) {
  element.addEventListener(eventType, listener as EventListener, options);
}

playground

kulphzqa

kulphzqa4#

我认为这次讨论的目标不应该只是"让我们探索解决方法",而更应该朝着"让我们将这个功能添加到Typescript或类型本身中"的方向发展。我们已经有了解决方法。

相关问题