How to assert a type of an HTMLElement in TypeScript?

231,351

Solution 1

TypeScript uses '<>' to surround casts, so the above becomes:

var script = <HTMLScriptElement>document.getElementsByName("script")[0];

However, unfortunately you cannot do:

var script = (<HTMLScriptElement[]>document.getElementsByName(id))[0];

You get the error

Cannot convert 'NodeList' to 'HTMLScriptElement[]'

But you can do :

(<HTMLScriptElement[]><any>document.getElementsByName(id))[0];

Solution 2

Do not type cast. Never. Use type guards:

const e = document.getElementsByName("script")[0];
if (!(e instanceof HTMLScriptElement)) 
  throw new Error(`Expected e to be an HTMLScriptElement, was ${e && e.constructor && e.constructor.name || e}`);
// locally TypeScript now types e as an HTMLScriptElement, same as if you casted it.

Let the compiler do the work for you and get errors when your assumptions turn out wrong.

It may look overkill in this case, but it will help you a lot if you come back later and change the selector, like adding a class that is missing in the dom, for example.

Solution 3

As of TypeScript 0.9 the lib.d.ts file uses specialized overload signatures that return the correct types for calls to getElementsByTagName.

This means you no longer need to use type assertions to change the type:

// No type assertions needed
var script: HTMLScriptElement = document.getElementsByTagName('script')[0];
alert(script.type);

Solution 4

You always can hack type system using:

var script = (<HTMLScriptElement[]><any>document.getElementsByName(id))[0];

Solution 5

We could type our variable with an explicit return type:

const script: HTMLScriptElement = document.getElementsByName(id).item(0);

Or assert as (needed with TSX):

const script = document.getElementsByName(id).item(0) as HTMLScriptElement;

Or in simpler cases assert with angle-bracket syntax.


A type assertion is like a type cast in other languages, but performs no special checking or restructuring of data. It has no runtime impact, and is used purely by the compiler.

Documentation:

TypeScript - Basic Types - Type assertions

Share:
231,351

Related videos on Youtube

Spongman
Author by

Spongman

Updated on May 16, 2021

Comments

  • Spongman
    Spongman about 3 years

    I'm trying to do this:

    var script:HTMLScriptElement = document.getElementsByName("script")[0];
    alert(script.type);
    

    but it's giving me an error:

    Cannot convert 'Node' to 'HTMLScriptElement': Type 'Node' is missing property 'defer' from type 'HTMLScriptElement'
    (elementName: string) => NodeList
    

    I can't access the 'type' member of the script element unless I cast it to the correct type, but I don't know how to do this. I searched the docs & samples, but I couldn't find anything.

    • Greg Gum
      Greg Gum over 10 years
      Note that this casting issue no longer exists in 0.9 - See answer by @Steve below.
    • Steve Schrab
      Steve Schrab about 7 years
      @GregGum I'm not seeing an answer by a Steve
  • Spongman
    Spongman over 11 years
    granted that is correct, however not entirely useful. the alternative is to go via 'any' which provides no useful type checking whatsoever...
  • rekna
    rekna over 11 years
    i think they should look into this further, suppose you use $('[type:input]').each( function(index,element) and you need element to be cast to HTMLInputElement or HTMLSelectElement depending on which property you need to set/get, casting use (<HTMLSelectElement><any>element).selectedIndex=0; adds () around element , kind of ugly
  • lhk
    lhk over 11 years
    +1 that answered my question stackoverflow.com/questions/13669404/…
  • Peter Burns
    Peter Burns about 11 years
    In the long run (after 0.9 is out) you should be able to cast it to something like NodeList<HtmlScriptElement>, plus getElementsByName will be able to use string literal type overrides to get this right without any casting at all!
  • Luke
    Luke almost 11 years
    any is just huge, without ts would just be a joke
  • Will Huang
    Will Huang almost 10 years
    after 1.0, the syntax should be (<NodeListOf<HTMLScriptElement>>document.getElementsByName(i‌​d))[0];
  • vilicvane
    vilicvane over 9 years
    any is the bridge, but it shouldn't be applied here. NodeList is not an array of Node, and shouldn't be treated as an array. A better way would be <Node[]>Array.prototype.slice.call(nodeList);
  • Laker
    Laker about 8 years
    Thank you. I'm sad that it has to be done like this, but it works. Better than nothing:/
  • Nikos
    Nikos over 7 years
    how do you do it in object notation? ie I can't do {name: <HTMLInputElement> : document.querySelector('#app-form [name]').value,}
  • Nikos
    Nikos over 7 years
    this worked: name: (<HTMLInputElement> document.querySelector('#app-form [name]')).value,
  • Mike Keesey
    Mike Keesey about 7 years
    UPDATE Casting now looks like this: const script = document.getElementsByName(id).item(0) as HTMLScriptElement;
  • markeissler
    markeissler about 7 years
    That is, "looks like this" for TS 2.3.
  • JGFMK
    JGFMK almost 7 years
    You can also use as to cast. var script = document.getElementsByName("script")[0] as HTMLScriptElement;
  • Daedalon
    Daedalon over 6 years
    The recommended type to use is HTMLInputElement according to github.com/Microsoft/TypeScript/issues/10453.
  • tit
    tit over 5 years
    using <any> allows escaping type checking, not ideal but cool while in development
  • Richard Hunter
    Richard Hunter over 3 years
    It would seem safe here, surely? We can guarantee that e is always going to be an instance of HTMLScriptElement, can't we (unless it doesn't exist, I suppose)?
  • Souleste
    Souleste almost 3 years
    I couldn't seem to get any type casting to work but this worked.
  • LuanLuanLuan
    LuanLuanLuan over 2 years
    Tks! the second option work for me. The first option my lint print: Type 'HTMLElement | null' is not assignable to type 'HTMLScriptElement'. Type 'null' is not assignable to type 'HTMLScriptElement'. (is my first project em Typescript :S hahaha)