Home > Net >  React onclick Argument of type 'EventTarget' is not assignable to parameter of type '
React onclick Argument of type 'EventTarget' is not assignable to parameter of type '

Time:02-20

I import MouseEvent from react

import { MouseEvent } from 'react';

Use MouseEvent in the following

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

I listen to my closeSelectBox

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

searchOptionWrapRef is a div

const searchOptionWrapRef = useRef<HTMLDivElement>(null);

<div ref={searchOptionWrapRef}/>

But I get the following error

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.

How do I resolve this type error without using any in place of MouseEvent?

CodePudding user response:

The event interfaces exported by React are for React event handler props, not addEventListener handlers. For those, don't import MouseEvent from React and you'll get the DOM global interface for it instead, which works with addEventListener. And yes, it's confusing. :-)

But the second issue (which actually may be your main issue) is that the DOM global MouseEvent defines target as an EventTarget, not as a Node. In your case, it'll always be a Node (specifically, an Element), but that's how the DOM type is defined. To deal with that, you have at least two choices:

Purist

You could go really purist (I do) and use a type assertion function to assert that target is a Node:

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

Concise and Pragmatic

You know that target is a Node and isn't null, so you could use a type assertion (target as Node):

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

Playground link

I don't like type assertions that aren't checked at runtime (which is what a type assertion function like assertIsNode does), so I'd probably go with the first approach. But in limited situations where you're sure about it, you might consider one.

  • Related