【问题标题】:React onclick Argument of type 'EventTarget' is not assignable to parameter of type 'Node'React onclick \'EventTarget\' 类型的参数不可分配给 \'Node\' 类型的参数
【发布时间】:2022-12-02 22:34:06
【问题描述】:

我从反应导入MouseEvent

import { MouseEvent } from 'react';

在下面使用MouseEvent

  const closeSelectBox = (e: MouseEvent): void => {
    if (!searchOptionWrapRef.current?.contains(e.target)) {
      setOpenSelectBox(false)
    }
  };

我听我的closeSelectBox

  useEffect(() => {
    document.addEventListener("click", closeSelectBox);
    return () => {
      document.removeEventListener("click", closeSelectBox);
    };
  }, [])

searchOptionWrapRef 是一个div

const searchOptionWrapRef = useRef<HTMLDivElement>(null);

<div ref={searchOptionWrapRef}/>

但我收到以下错误

Argument of type 'EventTarget' is not assignable to parameter of type 'Node'.
  Type 'EventTarget' is missing the following properties from type 'Node': baseURI, childNodes, firstChild, isConnected, and 43 more.

如何在不使用 any 代替 MouseEvent 的情况下解决此类型错误?

【问题讨论】:

    标签: reactjs typescript


    【解决方案1】:

    React 导出的事件接口用于 React 事件处理程序 props,而不是 addEventListener 处理程序。对于那些,从 React 导入 MouseEvent,你将获得它的 DOM 全局接口,它与 addEventListener 一起工作。是的,这令人困惑。 :-)

    但第二个问题(实际上可能是您的主要问题)是 DOM 全局 MouseEventtarget 定义为 EventTarget,而不是 Node。在您的情况下,它将始终是 Node(具体来说,Element),但这就是定义 DOM 类型的方式。为了解决这个问题,您至少有两个选择:

    纯粹主义者

    你可以变得非常纯粹(我这样做)并使用类型断言函数来断言 targetNode

    // In a utility library:
    function assertIsNode(e: EventTarget | null): asserts e is Node {
        if (!e || !("nodeType" in e)) {
            throw new Error(`Node expected`);
        }
    }
    
    // And then in your component:
    const closeSelectBox = ({target}: MouseEvent): void => {
        assertIsNode(target);
        if (!searchOptionWrapRef.current?.contains(target)) {
            setOpenSelectBox(false);
        }
    };
    

    Playground link

    简洁务实

    知道targetNode 而不是 null,因此您可以使用类型断言 (target as Node):

    const closeSelectBox = ({target}: MouseEvent): void => {
        if (!searchOptionWrapRef.current?.contains(target as Node)) {
            setOpenSelectBox(false);
        }
    };
    

    Playground link

    我不喜欢在运行时未检查的类型断言(这是 assertIsNode 之类的类型断言函数所做的),所以我可能会采用第一种方法。但是在您确定的有限情况下,您可能会考虑一个。

    【讨论】:

    • 什么是 DOM 全局接口?我应该只使用 any 代替 MouseEvent 吗?
    • @dev_el - DOM 全局接口是全局的,您不能从任何地方(据我所知)导入它。如果您想要类型安全,请不要使用 any。 :-) 但我正在做一个 TypeScript 游乐场示例,看起来还有第二个问题。我现在正在努力......
    • @dev_el =-现在完成,对此感到抱歉。我得出的结论是只是事件接口的选择是问题所在,但不仅如此。
    【解决方案2】:

    您可以使用instanceof断言该实例属于ElementHTMLElement。该解决方案以最小的方式解决了运行时和编译时检查,而不会导致异常。

    const closeSelectBox = (e: MouseEvent): void => {
      if (e.target instanceof HTMLElement && !searchOptionWrapRef.current?.contains(e.target)) {
        setOpenSelectBox(false)
      }
    };
    

    【讨论】:

      【解决方案3】:

      这解决了我的用例。我向打字稿断言“目标”是节点类型。

      function handleOutsideClickModal({ target }: MouseEvent) {
          if (!sidebar.current?.contains(target as Node)) {
              setOpenModal(!openModal);
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-10-17
        • 2020-03-26
        • 1970-01-01
        • 2022-08-24
        • 2019-06-24
        • 2021-06-14
        • 2020-09-03
        相关资源
        最近更新 更多