I have somewhere in my code:
const myElem: HTMLElement | null = document.getElementById("asd");
let arrow: any = [];
if (myElem) {
arrow = myElem.childNodes;
arrow[1].style.display = "none";
arrow[2].style.display = "block";
}
I'm attempting to replace the any type of the arrow variable. It needs to be declared and initialized before it gets a value. I attempted:
let arrow: HTMLCollectionOf<HTMLElement> = [];
arrow = myElem.childNodes as HTMLCollectionOf<HTMLElement>;
Because official documentation says: NodeList objects are collections of nodes, usually returned by properties such as Node.childNodes and methods such as document.querySelectorAll().
and
The read-only childNodes property of the Node interface returns a live NodeList of child nodes of the given element where the first child node is assigned index 0. Child nodes include elements, text and comments.
The errors I get with what I attempted:
error TS2739: Type 'never[]' is missing the following properties from type 'HTMLCollectionOf<HTMLElement>': item, namedItem
error TS2352: Conversion of type 'NodeListOf<ChildNode>' to type 'HTMLCollectionOf<HTMLElement>' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first. Property 'namedItem' is missing in type 'NodeListOf<ChildNode>' but required in type 'HTMLCollectionOf<HTMLElement>'.
If you have a particular reason to use childNodes
(such as wanting non-element nodes), set the type of arrow
to NodeListOf<ChildNode>
. All your error messages are hinting you what to do. It's like they're saying "You said arrow
would be a <X>, but then you assigned a value to it of type NodeListOf<ChildNode>
, which is not like a <X>". If you go this route, you'll also have to do a type assertion / type check to use the style
property, because that property is on the HTMLElement
type- not on the ChildNode
type.
Now this leaves the talk about your initialization.
If you're not going to use arrow
again after the body of that if
block, then just move the scope of arrow
to go inside the if
block. That way you don't even need to write the type annotation. TypeScript will infer it.
const myElem = document.getElementById("asd");
if (myElem) {
const arrow = myElem.childNodes;
(arrow[1] as HTMLElement).style.display = "none";
(arrow[2] as HTMLElement).style.display = "block";
}
If you will use arrow again after the body of that if
block, then you've better have the type reflect that it might not be initialized by the if
block, and what you initialize it to, which would then be the value. I'd suggest you use null
or undefined
instead of an empty array. It's a bit more conventional. Unless you have some great reason to use an empty array as the "default" value. Whatever you do, make the typings work. Ex.
const myElem = document.getElementById("asd");
let arrow: NodeListOf<ChildNode> | undefined = undefined;
if (myElem) {
arrow = myElem.childNodes;
(arrow[1] as HTMLElement).style.display = "none";
(arrow[2] as HTMLElement).style.display = "block";
}
Notice that I took out your manual typing of myElem
. It's redundant because it's identical to the return type of document.getElementById
.
If you don't have a particular reason to use childNodes
(such as wanting non-element nodes), you could also use HTMLElement.children()
, which returns a HTMLCollection
, but you'll have to do a type assertion / type check to HTMLElement
, since the type for elements of HTMLCollection
is Element
and not HTMLElement
.