March 9, 2016

TypeScript: cast HTMLElement

While working with document.getElementsByTagName() in TypeScript, I faced problems in casting. Finally I make it work after searcing different sites, and concluded those things in this post that could help someone facing the same problem.

Function document.getElementsByTagName() returns NodeListOf<Element>, Where Element is more generic type extends from type Node. Since TypeScript used lib.d.ts declaration file to recognize native javascript functions and properties, you can find this function's multiple signatures in lib.d.ts with different tag names, each have different return type. For example, document.getElementsByTagName("h1") will return NodeListOf<HTMLHeadingElement>.

Here is the list of some common html elements with corresponding varabiles casts.

 var nodeList1: NodeListOf = document.getElementsByTagName("h1");
 var nodeList2: NodeListOf = document.getElementsByTagName("p");
 var nodeList3: NodeListOf = document.getElementsByTagName("div");
 var nodeList4: NodeListOf = document.getElementsByTagName("a");
 var nodeList5: NodeListOf = document.getElementsByTagName("input");

NodeList is an array-like structure containing multiple items of Node, you can access individual item by passing a particular index. For example, following example will return the first element in the nodelist with tag name h1.

 var var1: HTMLHeadingElement = document.getElementsByTagName("h1")[0];

In inheritance tree of the Document Object Model in lib.d.ts file, you can find that Element extends the Node type, acting as more generic type. Which further extended by HTMLElement, and then all major elements, including in above listing, will extend from HTMLElement.

Same like C# (or other Object-Oriented Languages), you have to take care of proper inheritance structure while working with elements casting. Consider these variables:

 var var1: Element = document.getElementsByTagName("h1")[0];
 var var2: HTMLElement = document.getElementsByTagName("h1")[0];
 var var3: HTMLHeadingElement = document.getElementsByTagName("h1")[0];

If you try to assign var2 to var1:

    var1 = var2;

it will be ok, since HTMLElement extends from Element. But if you try to assign var1 to var2:

    var2 = var1;

you will get compile error as below:

 TS2322 (Error Code)
 Type 'Element' is not assignable to type 'HTMLElement'. Property 'accessKey' is missing in type 'Element'.
 
 //or in generic form, if you try similar thing with other elements.
 Type 'x' is not assignable to type 'y'. Property 'z' is missing in type 'x'.

Because compiler is looking for target type's properties, if any property is missing in source type, it will prompt similar error message showing the name of the missing property.

The function document.getElementsByTagName() always returns NodeListOf<T>, If you want to work on array, you can cast this to array in the following statement:

 var nodeList: HTMLElement[] = (<HTMLElement[]> <any> document.getElementsByTagName("h1"));

This returns all h1 elements in variable nodeList, the array of HTMLElement.

Or you can also iterate through nodeList array items in for loop. In the following example, it will iterate through all items in nodeList and add these items to a new array.

 var nodeList: NodeListOf = document.getElementsByTagName("h1");
 var elementList : Array = [];

 if (nodeList) {
  for (let i = 0; i < nodeList.length; i++) {
   
   let node : Node = nodeList[i];
   elementList.push(node as HTMLElement);
  }
 }

References:

http://www.typescriptlang.org

1 comment:

  1. good, thank you. But your article is from 2016. Is it still the same now?

    ReplyDelete