TypeScript: casting HTMLElement


197

Qualcuno sa come trasmettere in TypeScript?

Sto provando a fare questo:

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

ma mi sta dando un errore:

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

Non riesco ad accedere al membro 'type' dell'elemento script a meno che non lo esegua nel tipo corretto, ma non so come farlo. Ho cercato i documenti e gli esempi, ma non sono riuscito a trovare nulla.


Nota che questo problema di casting non esiste più in 0.9 - Vedi la risposta di @Steve di seguito.
Greg Gum,

@GregGum Non vedo una risposta da parte di Steve
Steve Schrab,

Risposte:


255

TypeScript usa '<>' per circondare i cast, quindi quanto sopra diventa:

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

Tuttavia, sfortunatamente non puoi fare:

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

Ottieni l'errore

Cannot convert 'NodeList' to 'HTMLScriptElement[]'

Ma puoi fare:

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

penso che dovrebbero esaminare ulteriormente questo, supponiamo che tu usi $ ('[tipo: input]'). ciascuno (funzione (indice, elemento) e hai bisogno di un elemento per essere lanciato su HTMLInputElement o HTMLSelectElement a seconda della proprietà che devi impostare / get, casting use (<HTMLSelectElement> <any> element) .selectedIndex = 0; aggiunge () attorno all'elemento, un po 'brutto
rekna

Uno che ha risposto alla mia domanda stackoverflow.com/questions/13669404/...
LHK

A lungo termine (dopo che è uscito 0.9) dovresti essere in grado di lanciarlo su qualcosa come NodeList <HtmlScriptElement>, inoltre getElementsByName sarà in grado di usare le sostituzioni di tipo letterale stringa per ottenere questo risultato senza alcun cast!
Peter Burns,

3
dopo 1.0, la sintassi dovrebbe essere(<NodeListOf<HTMLScriptElement>>document.getElementsByName(id))[0];
Will Huang,

1
Puoi anche usare come per lanciare. var script = document.getElementsByName ("script") [0] come HTMLScriptElement;
JGFMK,

36

A partire da TypeScript 0.9 il lib.d.tsfile utilizza firme di sovraccarico specializzate che restituiscono i tipi corretti per le chiamate getElementsByTagName.

Ciò significa che non è più necessario utilizzare le asserzioni di tipo per modificare il tipo:

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

come si fa nella notazione oggetto? cioè non posso fare {name: <HTMLInputElement>: document.querySelector ('# app-form [name]'). value,}
Nikos

3
questo ha funzionato: nome: (<HTMLInputElement> document.querySelector ('# app-form [name]')). value,
Nikos

21

Puoi sempre hackerare il sistema di tipi usando:

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

l'utilizzo di <any> consente il controllo del tipo di escape, non ideale ma interessante durante lo sviluppo
tit

21

Non digitare cast. Mai. Usa protezioni di tipo:

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.

Lascia che il compilatore faccia il lavoro per te e ottenga errori quando i tuoi presupposti risultano errati.

In questo caso potrebbe sembrare eccessivo, ma ti aiuterà molto se torni più tardi e cambi il selettore, ad esempio aggiungendo una classe mancante nel dom.


13

Per finire con:

  • un Arrayoggetto reale (non un NodeListvestito come un Array)
  • un elenco che è garantito per includere solo HTMLElements, non Nodeè forzato a HTMLElements
  • una calda sensazione confusa di fare la cosa giusta

Prova questo:

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);
        }
    }
}

Godere.


10

Potremmo digitare la nostra variabile con un tipo di ritorno esplicito :

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

O asserire come (necessario con TSX ):

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

O in casi più semplici affermare con la sintassi della parentesi angolare .


Un'asserzione di tipo è come un cast di tipo in altre lingue, ma non esegue controlli o ristrutturazioni speciali dei dati. Non ha alcun impatto sul runtime ed è utilizzato esclusivamente dal compilatore.

Documentazione:

TypeScript - Tipi di base - Asserzioni di tipo


9

Giusto per chiarire, questo è corretto.

Impossibile convertire "Elenco nodi" in "HTMLScriptElement []"

come NodeListnon è una matrice reale (per esempio non contiene .forEach, .slice, .push, ecc ...).

Pertanto, se si convertisse HTMLScriptElement[]nel sistema di tipi, non si otterrebbero errori di tipo se si provasse a chiamare Array.prototypemembri su di esso in fase di compilazione, ma fallirebbe in fase di esecuzione.


1
garantito che sia corretto, ma non del tutto utile. l'alternativa è passare attraverso 'any' che non fornisce alcun tipo di controllo utile di sorta ...
Spongman,

3

Questo sembra risolvere il problema, usando il [index: TYPE]tipo di accesso alla matrice, evviva.

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

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

1

Potrebbe essere risolto nel file di dichiarazione (lib.d.ts) se TypeScript definisse HTMLCollection invece di NodeList come tipo restituito.

DOM4 specifica anche questo come il tipo di ritorno corretto, ma le specifiche DOM meno recenti sono meno chiare.

Vedi anche http://typescript.codeplex.com/workitem/252


0

Dal momento che è un NodeList, non un Array, non dovresti davvero usare parentesi o casting Array. Il modo proprietà per ottenere il primo nodo è:

document.getElementsByName(id).item(0)

Puoi semplicemente lanciarlo:

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

Oppure estendi NodeList:

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

1
AGGIORNAMENTO Il casting ora è simile al seguente: const script = document.getElementsByName(id).item(0) as HTMLScriptElement;
Mike Keesey,

Cioè, "sembra così" per TS 2.3.
markeissler,

0

Consiglierei anche le guide del sito

https://www.sitepen.com/blog/2013/12/31/definitive-guide-to-typescript/ (vedi sotto) e https://www.sitepen.com/blog/2014/08/22/advanced classi-tipi -typescript-concetti-/

TypeScript consente inoltre di specificare diversi tipi di ritorno quando viene fornita una stringa esatta come argomento di una funzione. Ad esempio, la dichiarazione ambientale di TypeScript per il metodo createElement del DOM è simile alla seguente:

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

Ciò significa, in TypeScript, quando si chiama ad esempio document.createElement ('video'), TypeScript sa che il valore restituito è un HTMLVideoElement e sarà in grado di assicurarsi di interagire correttamente con l'API video DOM senza che sia necessario digitare assert.


0
var script = (<HTMLScriptElement[]><any>document.getElementsByName(id))[0];    
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.