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.


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];

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

¿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];

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.


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.


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


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.


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];

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


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

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.


0
var script = (<HTMLScriptElement[]><any>document.getElementsByName(id))[0];    
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.