'What should I use Array.from or spread operator [...] in Typescript
I have been playing with TypeScript and I ran into a problem I wrote a shorthand for querySelectorAll()
export function selectAll(DOMElement: string, parent = document): Array<HTMLElement> | null {
return [...parent.querySelectorAll(DOMElement)];
}
The above code giving me Type 'Element[]' is not assignable to type 'HTMLElement[]'.
Then I changed my code a little bit
export function selectAll(DOMElement: string, parent = document): Array<HTMLElement> | null {
return Array.from(parent.querySelectorAll(DOMElement));
}
Now I am not getting any error. So, my questions are -
- Why
Array<HTMLElement>didn't work butArray<Element>worked. - What should I use
[...]spread operator orArray.from().
In Addition
Like bogdanoff mentioned in comment
"from docs querySelectorAll returns a non-live NodeList containing Element so Array<Element> is right type."
Then why querySelector is okay with returning HTMLElement instead of just Element
export function selectAll(DOMElement: string, parent = document): HTMLElement | null {
return parent.querySelector(DOMElement);
}
Solution 1:[1]
what happens behind the hoods
In order to understand what is going on, we need to look at how typescript defines the signature for Array.from and querySelectorAll. Excerpts:
from<T>(arrayLike: ArrayLike<T>): T[];
querySelectorAll<E extends Element = Element>(selectors: string): NodeListOf<E>;
For Array.from, it means that, given a type T, it will return an array of elements with type T. If no T is given, it could be anything.
Specifying a type for the return value of the function is actually saying: "please take T = HTMLElement". In turn, that implies the argument arrayLike will have type ArrayLike<HTMLElement>.
NodeListOf<E> will match this by taking E = HTMLElement, which does extend Element, so it is valid, and typescript is happy.
why does typescript behave so?
Because you are deliberately giving the HTMLElement type for the return value, typescript trusts you and is capable to give specific types to E and T.
to go beyond
Interestingly, the following code would raise a typescript error:
function selectAll(DOMElement: string, parent = document): Array<HTMLElement> {
const all = parent.querySelectorAll(DOMElement);
return Array.from(all);
}
This is because on the line defining all, there is no information whatsoever about what type E could be, so it defaults to being Element. When parsing the return line, TS will (rightly) complain that Element cannot map to HTMLElement.
Conclusion
To your questions:
- use spread operator or Array.from => in your case, spread operator is not an option since it raises an error, which
Array.fromdoes not - why querySelector is okay with returning HTMLElement instead of just Element? => hopefully, the above has clarified why
Solution 2:[2]
I played around with the snippet you've provided and I think Array.from casts Element[] into HTMLElement[]
function selectAll(DOMElement: string, parent = document): Array<HTMLElement> | null {
const m : Array<HTMLElement> | null= Array.from(parent.querySelectorAll(DOMElement));
return m
}
function selectAll2(DOMElement: string, parent = document): Array<HTMLElement> | null {
const m = Array.from(parent.querySelectorAll(DOMElement));
return m
}
If you check selectAll2 will throw an error.
And this won't work in
const arr = ["a","b"];
const setup1 = () : number[] => {
return [...arr]
}
const setup1 = () : number[] => {
return Array.from(arr)
}
cause HTMLElement is a child class of Element[] itself.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | Lakshya |
