TypeScript: casting HTMLElement

197

¿Alguien sabe cómo transmitir en TypeScript?

Estoy tratando de hacer esto:

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

pero me está dando un error:

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

No puedo acceder al miembro 'tipo' del elemento de script a menos que lo convierta al tipo correcto, pero no sé cómo hacerlo. Busqué en los documentos y muestras, pero no pude encontrar nada.

Spongman
fuente
Tenga en cuenta que este problema de transmisión ya no existe en 0.9: consulte la respuesta de @Steve a continuación.
Greg Gum
@GregGum No veo una respuesta de Steve
Steve Schrab

Respuestas:

255

TypeScript usa '<>' para rodear los lanzamientos, por lo que lo anterior se convierte en:

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

Sin embargo, desafortunadamente no puedes hacer:

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

Obtienes el error

Cannot convert 'NodeList' to 'HTMLScriptElement[]'

Pero puedes hacer:

(<HTMLScriptElement[]><any>document.getElementsByName(id))[0];
Spongman
fuente
creo que deberían investigar esto más a fondo, supongamos que usa $ ('[type: input]'). each (function (index, element) y necesita que el elemento se convierta en HTMLInputElement o HTMLSelectElement dependiendo de la propiedad que necesite establecer / get, uso de conversión (<HTMLSelectElement> <any> element) .selectedIndex = 0; agrega () alrededor del elemento, algo feo
rekna
+1 que respondió a mi pregunta stackoverflow.com/questions/13669404/…
lhk
A la larga (después de que 0.9 esté fuera), debería poder convertirlo en algo como NodeList <HtmlScriptElement>, ¡además getElementsByName podrá usar anulaciones de tipo literal de cadena para hacerlo correctamente sin ningún tipo de conversión!
Peter Burns el
3
después de 1.0, la sintaxis debería ser(<NodeListOf<HTMLScriptElement>>document.getElementsByName(id))[0];
Will Huang
1
También puedes usar como para lanzar. var script = document.getElementsByName ("script") [0] como HTMLScriptElement;
JGFMK
36

A partir de TypeScript 0.9, el lib.d.tsarchivo utiliza firmas especializadas de sobrecarga que devuelven los tipos correctos para las llamadas getElementsByTagName.

Esto significa que ya no necesita usar aserciones de tipo para cambiar el tipo:

// No type assertions needed
var script: HTMLScriptElement = document.getElementsByTagName('script')[0];
alert(script.type);
Fenton
fuente
¿Cómo lo haces en notación de objetos? es decir, no puedo hacer {name: <HTMLInputElement>: document.querySelector ('# app-form [name]'). value,}
Nikos
3
esto funcionó: nombre: (<HTMLInputElement> document.querySelector ('# app-form [name]')). value,
Nikos
21

Siempre puedes hackear el sistema de tipos usando:

var script = (<HTMLScriptElement[]><any>document.getElementsByName(id))[0];
Jack128
fuente
El uso de <any> permite la verificación de tipo de escape, no ideal pero genial durante el desarrollo
tit
21

No escriba cast. Nunca. Use guardias 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.

Deje que el compilador haga el trabajo por usted y obtenga errores cuando sus suposiciones resulten incorrectas.

Puede parecer excesivo en este caso, pero te ayudará mucho si vuelves más tarde y cambias el selector, como agregar una clase que falta en el dom, por ejemplo.

Benoit B.
fuente
13

Para terminar con:

  • un Arrayobjeto real (no un NodeListdisfrazado como unArray )
  • una lista que se garantiza que solo incluya HTMLElements, no Nodeforzada aHTMLElement s
  • una cálida sensación difusa de hacer lo correcto

Prueba esto:

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

Disfrutar.

Johannes Fahrenkrug
fuente
10

Podríamos escribir nuestra variable con un tipo de retorno explícito :

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

O afirmar como (necesario con TSX ):

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

O, en casos más simples, afirmar con sintaxis de paréntesis angular .


Una aserción de tipo es como una conversión de tipo en otros idiomas, pero no realiza ninguna comprobación especial o reestructuración de datos. No tiene impacto en el tiempo de ejecución, y es utilizado exclusivamente por el compilador.

Documentación:

TypeScript - Tipos básicos - Aserciones de tipo

León
fuente
9

Solo para aclarar, esto es correcto.

No se puede convertir 'NodeList' a 'HTMLScriptElement []'

como NodeListno es una matriz real (por ejemplo, que no contiene .forEach, .slice, .push, etc ...).

Por lo tanto, si se convirtió HTMLScriptElement[]en el sistema de tipos, no obtendría errores de tipo si intentara llamar a los Array.prototypemiembros en el momento de la compilación, pero fallaría en el tiempo de ejecución.

Bill Ticehurst
fuente
1
concedido que es correcto, sin embargo, no del todo útil. la alternativa es ir a través de 'cualquiera' que proporciona ningún tipo útil comprobar en absoluto ...
Spongman
3

Esto parece resolver el problema, utilizando el [index: TYPE]tipo de acceso a matriz, saludos.

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

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

Podría resolverse en el archivo de declaración (lib.d.ts) si TypeScript definiría HTMLCollection en lugar de NodeList como un tipo de retorno.

DOM4 también especifica esto como el tipo de retorno correcto, pero las especificaciones DOM más antiguas son menos claras.

Ver también http://typescript.codeplex.com/workitem/252

Peter
fuente
0

Dado que es un NodeList, no un Array, realmente no deberías usar corchetes o lanzar Array. La forma de propiedad para obtener el primer nodo es:

document.getElementsByName(id).item(0)

Puedes lanzar eso:

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

O extienda NodeList:

interface HTMLScriptElementNodeList extends NodeList
{
    item(index: number): HTMLScriptElement;
}
var scripts = <HTMLScriptElementNodeList> document.getElementsByName('script'),
    script = scripts.item(0);
Mike Keesey
fuente
1
ACTUALIZACIÓN El casting ahora se ve así: const script = document.getElementsByName(id).item(0) as HTMLScriptElement;
Mike Keesey
Es decir, "se ve así" para TS 2.3.
markeissler
0

También recomendaría las guías del sitio.

https://www.sitepen.com/blog/2013/12/31/definitive-guide-to-typescript/ (ver a continuación) y https://www.sitepen.com/blog/2014/08/22/advanced -typescript-concepts-classes-types /

TypeScript también le permite especificar diferentes tipos de retorno cuando se proporciona una cadena exacta como argumento para una función. Por ejemplo, la declaración ambiental de TypeScript para el método createElement del DOM se ve así:

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

Esto significa que, en TypeScript, cuando llama, por ejemplo, document.createElement ('video'), TypeScript sabe que el valor de retorno es un HTMLVideoElement y podrá asegurarse de que está interactuando correctamente con la API de video DOM sin necesidad de escribir aserción.

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