TypeScript: cast HTMLElement


197

Quelqu'un sait-il comment transtyper en TypeScript?

J'essaye de faire ça:

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

mais ça me donne une erreur:

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

Je ne peux pas accéder au membre «type» de l'élément de script à moins que je ne le transforme en type correct, mais je ne sais pas comment faire. J'ai cherché dans les documents et les échantillons, mais je n'ai rien trouvé.


Notez que ce problème de casting n'existe plus en 0.9 - Voir la réponse de @Steve ci-dessous.
Greg Gum

@GregGum Je ne vois pas de réponse d'un Steve
Steve Schrab

Réponses:


255

TypeScript utilise '<>' pour entourer les transtypages, donc ce qui précède devient:

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

Cependant, malheureusement, vous ne pouvez pas faire:

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

Vous obtenez l'erreur

Cannot convert 'NodeList' to 'HTMLScriptElement[]'

Mais vous pouvez faire:

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

Je pense qu'ils devraient approfondir cela, supposons que vous utilisiez $ ('[type: input]'). each (fonction (index, élément) et que vous ayez besoin que l'élément soit casté en HTMLInputElement ou HTMLSelectElement selon la propriété que vous devez définir / get, transtypage use (<HTMLSelectElement> <any> élément) .selectedIndex = 0; ajoute () autour de l'élément, sorte de laid
rekna

+1 qui a répondu à ma question stackoverflow.com/questions/13669404/…
lhk

À long terme (après la sortie de 0.9), vous devriez pouvoir le convertir en quelque chose comme NodeList <HtmlScriptElement>, plus getElementsByName sera en mesure d'utiliser des remplacements de type littéral de chaîne pour obtenir ce droit sans aucune conversion du tout!
Peter Burns

3
après 1.0, la syntaxe devrait être(<NodeListOf<HTMLScriptElement>>document.getElementsByName(id))[0];
Will Huang

1
Vous pouvez également utiliser as pour lancer. var script = document.getElementsByName ("script") [0] as HTMLScriptElement;
JGFMK

36

Depuis TypeScript 0.9, le lib.d.tsfichier utilise des signatures de surcharge spécialisées qui renvoient les types corrects pour les appels vers getElementsByTagName.

Cela signifie que vous n'avez plus besoin d'utiliser des assertions de type pour modifier le type:

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

comment faites-vous en notation objet? c'est-à-dire que je ne peux pas faire {name: <HTMLInputElement>: document.querySelector ('# app-form [name]'). value,}
Nikos

3
cela a fonctionné: nom: (<HTMLInputElement> document.querySelector ('# app-form [name]')). value,
Nikos

21

Vous pouvez toujours pirater le système de type en utilisant:

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

utiliser <any> permet d'échapper à la vérification de type, pas idéal mais cool pendant le développement
tit

21

Ne tapez pas cast. Jamais. Utilisez des protecteurs de type:

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.

Laissez le compilateur faire le travail pour vous et obtenez des erreurs lorsque vos hypothèses s'avèrent erronées.

Cela peut sembler exagéré dans ce cas, mais cela vous aidera beaucoup si vous revenez plus tard et changez le sélecteur, comme l'ajout d'une classe qui manque dans le dom, par exemple.


13

Pour finir avec:

  • un Arrayobjet réel (pas un NodeListdéguisé en Array)
  • une liste qui est garantie de n'inclure que HTMLElements, et non d'être Nodeconvertie de force en HTMLElements
  • une sensation floue et chaleureuse de faire la bonne chose

Essaye ça:

let nodeList : NodeList = document.getElementsByTagName('script');
let elementList : Array<HTMLElement> = [];

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

        // Make sure it's really an Element
        if (node.nodeType == Node.ELEMENT_NODE) {
            elementList.push(node as HTMLElement);
        }
    }
}

Prendre plaisir.


10

Nous pourrions taper notre variable avec un type de retour explicite :

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

Ou affirmez comme (nécessaire avec TSX ):

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

Ou dans des cas plus simples, affirmez avec la syntaxe de parenthèse angulaire .


Une assertion de type est comme un type transtypé dans d'autres langues, mais n'effectue aucune vérification ou restructuration particulière des données. Il n'a aucun impact sur l'exécution et est utilisé uniquement par le compilateur.

Documentation:

TypeScript - Types de base - Assertions de type


9

Juste pour clarifier, c'est correct.

Impossible de convertir 'NodeList' en 'HTMLScriptElement []'

comme NodeListn'est pas un tableau réel (par exemple , il ne contient pas .forEach, .slice, .push, etc ...).

Ainsi, s'il se convertissait en HTMLScriptElement[]dans le système de type, vous n'obtiendrez aucune erreur de type si vous essayez d'appeler des Array.prototypemembres dessus au moment de la compilation, mais il échouera au moment de l'exécution.


1
accordé qui est correct, mais pas entièrement utile. l'alternative est de passer par 'any' qui ne fournit aucune vérification de type utile ...
Spongman

3

Cela semble résoudre le problème, en utilisant le [index: TYPE]type d'accès au tableau, cheers.

interface ScriptNodeList extends NodeList {
    [index: number]: HTMLScriptElement;
}

var script = ( <ScriptNodeList>document.getElementsByName('foo') )[0];

1

Pourrait être résolu dans le fichier de déclaration (lib.d.ts) si TypeScript définissait HTMLCollection au lieu de NodeList comme type de retour.

DOM4 spécifie également cela comme type de retour correct, mais les anciennes spécifications DOM sont moins claires.

Voir aussi http://typescript.codeplex.com/workitem/252


0

Comme c'est un NodeList, pas un Array, vous ne devriez pas vraiment utiliser de parenthèses ou de casting Array. La propriété pour obtenir le premier nœud est la suivante:

document.getElementsByName(id).item(0)

Vous pouvez simplement lancer cela:

var script = <HTMLScriptElement> document.getElementsByName(id).item(0)

Ou étendez NodeList:

interface HTMLScriptElementNodeList extends NodeList
{
    item(index: number): HTMLScriptElement;
}
var scripts = <HTMLScriptElementNodeList> document.getElementsByName('script'),
    script = scripts.item(0);

1
MISE À JOUR Le casting ressemble maintenant à ceci: const script = document.getElementsByName(id).item(0) as HTMLScriptElement;
Mike Keesey

Autrement dit, "ressemble à ceci" pour TS 2.3.
markeissler

0

Je recommanderais également les guides sitepen

https://www.sitepen.com/blog/2013/12/31/definitive-guide-to-typescript/ (voir ci-dessous) et https://www.sitepen.com/blog/2014/08/22/advanced -typescript-concepts-classes-types /

TypeScript vous permet également de spécifier différents types de retour lorsqu'une chaîne exacte est fournie comme argument d'une fonction. Par exemple, la déclaration ambiante de TypeScript pour la méthode createElement du DOM ressemble à ceci:

createElement(tagName: 'a'): HTMLAnchorElement;
createElement(tagName: 'abbr'): HTMLElement;
createElement(tagName: 'address'): HTMLElement;
createElement(tagName: 'area'): HTMLAreaElement;
// ... etc.
createElement(tagName: string): HTMLElement;

Cela signifie que, dans TypeScript, lorsque vous appelez par exemple document.createElement ('video'), TypeScript sait que la valeur de retour est un HTMLVideoElement et sera en mesure de vous assurer que vous interagissez correctement avec l'API DOM Video sans avoir besoin de taper assert.


0
var script = (<HTMLScriptElement[]><any>document.getElementsByName(id))[0];    
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.