Does anyone know how to cast in TypeScript?
I\'m trying to do this:
var script:HTMLScriptElement = document.getElementsByName(\"script\")[0];
alert(s
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
I would also recommend the sitepen guides
https://www.sitepen.com/blog/2013/12/31/definitive-guide-to-typescript/ (see below) and https://www.sitepen.com/blog/2014/08/22/advanced-typescript-concepts-classes-types/
TypeScript also allows you to specify different return types when an exact string is provided as an argument to a function. For example, TypeScript’s ambient declaration for the DOM’s createElement method looks like this:
createElement(tagName: 'a'): HTMLAnchorElement;
createElement(tagName: 'abbr'): HTMLElement;
createElement(tagName: 'address'): HTMLElement;
createElement(tagName: 'area'): HTMLAreaElement;
// ... etc.
createElement(tagName: string): HTMLElement;
This means, in TypeScript, when you call e.g. document.createElement('video'), TypeScript knows the return value is an HTMLVideoElement and will be able to ensure you are interacting correctly with the DOM Video API without any need to type assert.
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);
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.
Rather than using a type assertion, type guard, or any
to work around the issue, a more elegant solution would be to use generics to indicate the type of element you're selecting.
Unfortunately, getElementsByName
is not generic, but querySelector
and querySelectorAll
are. (querySelector
and querySelectorAll
are also far more flexible, and so might be preferable in most cases.)
If you pass a tag name alone into querySelector
or querySelectorAll
, it will automatically be typed properly due to the following line in lib.dom.d.ts
:
querySelector<K extends keyof HTMLElementTagNameMap>(selectors: K): HTMLElementTagNameMap[K] | null;
For example, to select the first script tag on the page, as in your question, you can do:
const script = document.querySelector('script')!;
And that's it - TypeScript can now infer that script
is now an HTMLScriptElement
.
Use querySelector
when you need to select a single element. If you need to select multiple elements, use querySelectorAll
. For example:
document.querySelectorAll('script')
results in a type of NodeListOf<HTMLScriptElement>
.
If you need a more complicated selector, you can pass a type parameter to indicate the type of the element you're going to select. For example:
const ageInput = document.querySelector<HTMLInputElement>('form input[name="age"]')!;
results in ageInput
being typed as an HTMLInputElement
.
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];