Typescript:区分 HTMLElement 和 SVGElement 的 addEventListener

Typescript: differentiating HTMLElement's and SVGElement's addEventListener

我正在编写一个 class,它可以接受 HTMLElementSVGElement,并且想使用具有相同功能的 addEventListener 函数进行绑定和事件两种类型的签名。 我在下面隔离了一个测试用例。

我的问题:是否可以在不使用 instanceof 的情况下获得 eventListener event 参数的正确类型?


type HTMLorSVGElement = HTMLElement | SVGElement;

function test() {

  const htmlEl = document.querySelector('div');
  const svgEl = document.querySelector('svg');

  const randomEl: HTMLorSVGElement | null = Math.random() > 0.5 ? htmlEl : svgEl;

  function eventListener(event: MouseEvent) {
    console.log(event.clientX);
  }

  /*
  This usage produces the folowing error

  Error:(12, 39) TS2345: Argument of type '(event: MouseEvent) => void' is not assignable to parameter of type 'EventListenerOrEventListenerObject'.
    Type '(event: MouseEvent) => void' is not assignable to type 'EventListener'.
    Types of parameters 'event' and 'evt' are incompatible.
    Type 'Event' is missing the following properties from type 'MouseEvent': altKey, button, buttons, clientX, and 20 more.
  */
  randomEl?.addEventListener('click', eventListener);

  /*
   This doesn't produce an error
   */
  if (randomEl instanceof HTMLElement) {
    randomEl.addEventListener('click', eventListener);
  } else if (randomEl instanceof SVGElement) {
    randomEl.addEventListener('click', eventListener);
  }
}

发生这种情况是因为当我们将 strictFunctionTypes flag 设置为 true 时,TypeScript 编译器会感到困惑。将 strict 设置为 true 也会设置此标志。

如错误消息所述,编译器对是否可以将您的 eventListener 函数(MouseEvent -> void)分配给一种类型的 EventListener 感到困惑,尽管它完全能够处理不太复杂的场景中的语法.

有一个简单的解决方法可以使代码编译并工作:

randomEl?.addEventListener('click', eventListener as EventListener);

或者我们可以在 tsconfig.

中使用 "strictFunctionTypes": false 关闭麻烦的标志。

我已经更新了 my CodePen 所以它可以在 strict 设置为 true 的情况下工作,尽管我认为 CodePen 站点实际上不允许您设置 strict 标志。如果您在本地复制代码,它应该可以工作。

有一个 open issue on the TypeScript github 与此相关(我是从哪里得到答案的!)。