Gestión del DOM

<< Tipado avanzado Integración de librerías >>

En esta sección, vamos a ver qué retos podemos encontrarnos a la hora de trabajar con el DOM en TypeScript y como funciona el tipado de los elementos HTML.

HTMLElement

Para tipar los objetos del DOM, TypeScript se apoya en interfaces. Se utiliza herencia y polimorfismo a la hora de gestionar los tipos de dichos objetos. Por ejemplo, los métodos de búsqueda de elementos del DOM, tipan dichos elementos con la interfaz HTMLElement

Esta interfaz es común a todos los elementos HTML del DOM. Es decir, solo tiene los atributos y métodos comunes, y no los específicos (Polimorfismo). Si queremos especificar el tipo de elemento, se lo tenemos que indicar explícitamente. Esto se puede hacer mediante el uso de tipos genéricos o mediante un casting explícito.

 Importante: el tipado con genéricos solo funciona en métodos modernos como querySelector o querySelectorAll.

const img = document.getElementById("img"); // HTMLElement | null
img!.src = "nueva ruta"; // Property 'src' does not exist on type 'HTMLElement'.

const img2 = document.querySelector<HTMLImageElement>("#img"); // HTMLImageElement | null
img2!.src = "nueva ruta"; // OK

const img3 = document.getElementById("#img") as HTMLImageElement;
img3.src = "nueva ruta"; // OK

Los tipos derivados de HTMLElement tienen un nombre que contiene el tipo de elemento como: HTMLInputElement, HTMLImageElement, HTMLButtonElement, HTMLFormElement, etc.

Creando elementos del DOM

A la hora de usar document.createElement("etiqueta") para crear elementos del DOM, no debemos preocuparnos, ya que TypeScript infiere el tipo a partir del nombre de la etiqueta.

const img = document.createElement("img"); // Devuelve un HTMLImageElement
img.src = "Ruta Imagen"; // OK

Conflictos de tipos

Existen situaciones especiales donde se nos va a dificular el acceso a las propiedades de un objeto debido a la ambigüedad en el tipado. Por ejemplo, si el campo de un formulario se llama "name" o "title", y queremos acceder al mismo a partir del objeto del formulario, podemos tener un problema.

En el caso que utilicemos la sintaxis formulario.nombreCampo, TypeScript nos lo permite pero nos devuelve un elemento tipado como any, por lo que simplemente lo asociamos al tipo correcto y arreglado.

const form = document.getElementById("form") as HTMLFormElement;
const inputPrecio = form.precio as HTMLInputElement;

Sin embargo, si el campo se llama "name" o "title", TypeScript pensará (con razón) que estamos intentando acceder al atributo "name" o "title" (que tienen todos los elementos HTML). Por esto, nos lo tipara como string. Al ser incompatibles los tipos (no son derivados), no dejará convertir de string a HTMLInputElement, o HTMLSelectElement, etc.

Una solución para estos casos, es convertir a unknown el tipo (es como quitarselo), y después convertir al tipo correcto.

const form = document.getElementById("form") as HTMLFormElement;
const inputName = form.name as unknown as HTMLInputElement;

Otra solución más limpia sería acceder a los campos del formulario a través de su colección elements, que permite buscar el campo del formulario por nombre o id sin entrar en conflicto con los atributos HTML.

const form = document.getElementById("form") as HTMLFormElement;
const inputName = form.elements.namedItem("name") as HTMLInputElement;

<< Tipado avanzado Integración de librerías >>