How to get “Element” -type value from “Element.parentNode” instead of “Node & ParentNode”?



The recipe of event delegating from http://youmightnotneedjquery.com/:

document.addEventListener(eventName, function(e) {
    // loop parent nodes from the target to the delegation node
    for (var target = e.target; target && target != this; target = target.parentNode) {
        if (target.matches(elementSelector)) {
            handler.call(target, e);
            break;
        }
    }
}, false);

I am trying rewrite it to TypeScript type-safely (for click event for now):

export default function delegateClickEventHandling(
    {
      clickTargetSelector,
      container = document
    }: {
      clickTargetSelector: string;
      container: HTMLElement | Document;
    },
    handler: (event: MouseEvent) => void
): void {

  container.addEventListener("click", (event: Event): void => {

    if (!(event instanceof MouseEvent)) {
      return;
    }

    for (
        let targetParentNode: Element | null = event.target as Element;
        isNotNull(targetParentNode) && targetParentNode !== event.currentTarget;
        targetParentNode = targetParentNode.parentNode
    ) {

      if (targetParentNode.matches(clickTargetSelector)) {
        handler(event);
      }
    }
  }, false);
}

TypeScript compiler tells me:

TS2322: Type '(Node & ParentNode) | null' is not assignable to type 'Element | null'.
Type 'Node & ParentNode' is not assignable to type 'Element | null'.
Type 'Node & ParentNode' is missing the following properties from type 'Element':
 assignedSlot, attributes, classList, className, and 64 more.

The .matches() is the method of the Element – I can’t to call it from Node & ParentNode). What have I do?

If targetParentNode = targetParentNode.parentNode as Element is completely all right please explain why.

P. S. Please note that any, object and type annotation omitting are not allowed.

Answer

You just need to cast targetParentNode.parentNode to Element.

So for loop might be look like this:

for (
    let targetParentNode: Element = event.target as Element;
    targetParentNode !== event.currentTarget;
    targetParentNode = targetParentNode.parentNode as Element
) {

  if (targetParentNode.matches(clickTargetSelector)) {
    handler(event);
  }
}

PlaygrounLink



Source: stackoverflow