React onclick Argument of type ‘EventTarget’ is not assignable to parameter of type ‘Node’

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.

Leave a Comment

Hata!: SQLSTATE[HY000] [1045] Access denied for user 'divattrend_liink'@'localhost' (using password: YES)