6
defineProperties(Element.prototype, {
    querySelector: {
        value: querySelectorPatched,
        writable: true,
        enumerable: true,
        configurable: true,
    },
    querySelectorAll: {
        value(this: HTMLBodyElement): NodeListOf<Element> {
            const nodeList = arrayFromCollection(
                elementQuerySelectorAll.apply(this, ArraySlice.call(arguments) as [string])
            );

            if (!featureFlags.ENABLE_NODE_LIST_PATCH) {
                const filteredResults = getFilteredArrayOfNodes(
                    this,
                    nodeList,
                    ShadowDomSemantic.Disabled
                );
                return createStaticNodeList(filteredResults);
            }

            return createStaticNodeList(
                getFilteredArrayOfNodes(this, nodeList, ShadowDomSemantic.Enabled)
            );
        },
        writable: true,
        enumerable: true,
        configurable: true,
    },

});

I want to understand this part of the following Typescript code here:

value(this: HTMLBodyElement): NodeListOf<Element>

I believe this code is overriding the standard querySelectorAll method. But the method is being defined on an Element, yet the value for this in the function is HTMLBodyElement. Does this mean the method can only be called from a body element? Or perhaps it's called from Element and then casted to HTMLBodyElement? Can someone explain the logic of this code? This is a part of the following repository.

1
  • I don't think it would make much of a difference to have it be an HTMLElement. Might have a few useful properties that a HTMLElement doesn't. HTMLBodyElement plays a more important role in the getFilteredArrayOfNodes function just above, where it is used to check if the node in question has the lwc patch enabled. Commented Nov 27, 2021 at 19:09

1 Answer 1

1

Nobody will explain this better than the developers of that project, but it totally looks like a mistake to me.

this is only being used in three function calls of two different types, in which it is passed as one parameter:

function querySelectorAll<E extends Element = Element>(selectors: string): NodeListOf<E>;

function getFilteredArrayOfNodes<T extends Node>(
    context: Element,
    unfilteredNodes: T[],
    shadowDomSemantic: ShadowDomSemantic
): T[]

When passing this typed as HTMLBodyElement to each of these functions, the type is actually always widened as Element, even in querySelectorAll because there is no type provided between angle brackets and no possible inference so the resolved E type is Element (the default type).

According to me, value(this: HTMLBodyElement) should be value(this: Element) because it is meant to override Element.prototype.querySelectorAll.

Doing so doesn't break anything in the code and is much more correct.

Sign up to request clarification or add additional context in comments.

4 Comments

Can you show how this method would be called say from a non body element, say from div? And what would happen to this when method is called?
Well this is always typed as it is declared so whatever the actual element type is, the type of this is HTMLBodyElement in value. However it does not really matter because the type gets widened to the most generic type Element when being passed as a parameter to querySelectorAll and getFilteredArrayOfNodes. So as I said, no impact when changing HTMLBodyElement to Element like it should be. The code will just work as it did, it's just a matter of code clarity for the developers.
Where did you identify that the parameter is passed to these methods?
In the code this appears three times, always as a parameter of a function call

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.