Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeScript: ev.target and Node.contains: EventTarget is not assignable to Node

Here's the code:

function bodyClickHandler(ev: MouseEvent) {
    if(containerRef.current && containerRef.current.contains(ev.target)) return;
    setOpen(false);
}

The error:

Argument of type 'EventTarget | null' is not assignable to parameter of type 'Node | null'. Type 'EventTarget' is missing the following properties from type 'Node': baseURI, childNodes, firstChild, isConnected, and 44 more.ts(2345)

containerRef.current is a HTMLDivElement. .contains ought to be this function.

Is ev.target not guaranteed to be a Node of some sort?

I can cast it (ev.target as Node) but I'm trying to figure out in what scenario this could possibly fail? What can I click on that isn't a Node?

like image 255
mpen Avatar asked Apr 11 '20 21:04

mpen


2 Answers

Short answer: It's due to poor typings on their part. Go ahead and cast it.

Long answer: The reason this happens is because if you click on an <svg /> inside a button (for example), the event.target will point to the SVG, which is not guaranteed to be a button (obviously). Only an event's currentTarget points to the element the handler's on. Therefore the people who made the React typings decided that they would define and event's currentTarget as what the button was in our example, but make no guarantees about the event's target except that it is an EventTarget. Therefore you are stuck with having to cast it. This is discussed in this PR.

like image 77
Robert Moore Avatar answered Sep 21 '22 14:09

Robert Moore


I'm just starting with Typescript. I have a similar problem, my solution is, I know that the event is always fired by a DomElement.

And this is how I inform Typescript:

function bodyClickHandler(ev: MouseEvent) {
    if(containerRef.current && containerRef.current.contains(ev.target as HTMLElement)) return;
    setOpen(false);
}

If you prefer to separate it:

function bodyClickHandler(ev: MouseEvent) {
    const target = ev.target as HTMLElement;
    if(containerRef.current && containerRef.current.contains(target)) return;
    setOpen(false);
}
like image 21
stripTM Avatar answered Sep 19 '22 14:09

stripTM