Maquetación Web Dinámica / TypeScript en la maquetación
Setup con Vite y tipos del DOM
Para escribir TypeScript necesitas un proyecto que lo compile — y el más
rápido de montar es Vite. Con eso listo, atacamos el caso que el maquetador
vive a diario: tipar los elementos del DOM. querySelector te devuelve
algo genérico (¡y que puede ser null!), y TypeScript te obliga —para tu
bien— a ser específico y honesto con esos dos hechos.
Setup en un minuto: Vite
# crear un proyecto TypeScript vanilla (sin framework)
npm create vite@latest mi-proyecto -- --template vanilla-ts
cd mi-proyecto
npm install
npm run dev # servidor de desarrollo con recarga en vivoEso te da un proyecto con TypeScript ya configurado, compilación
instantánea y un tsconfig.json que conviene tener en modo estricto
("strict": true — la plantilla de Vite ya lo trae). El modo estricto es
justo el que te obliga a manejar los null y no dejar tipos sueltos: es la
red de seguridad completa, y este curso siempre lo asume activo.
querySelector te da una caja genérica
// SIN especificar: TS lo trata como Element genérico
const generico = document.querySelector(".campo");
generico.value = ""; // ❌ Error: Property 'value' does not exist on type 'Element'
// CON el genérico de querySelector: le dices qué tipo esperas (preferido)
const input = document.querySelector<HTMLInputElement>(".campo");
input.value = ""; // ✓ .value existe... pero ojo, ver el null abajo
// alternativa: aserción "as" cuando estás seguro del tipo
const boton = document.querySelector(".btn") as HTMLButtonElement;Los tipos que más usarás son intuitivos: HTMLInputElement (tiene
.value, .checked), HTMLButtonElement, HTMLFormElement,
HTMLImageElement (tiene .src, .alt), HTMLAnchorElement (.href), y
el genérico HTMLElement (tiene .style, .classList, .textContent —
lo común a todos). El editor te autocompleta el nombre correcto.
El null honesto: querySelector puede no encontrar nada
Aquí TypeScript te salva de un clásico: querySelector devuelve el
elemento o null si nada coincide. En modo estricto, TS no te deja
ignorarlo:
const input = document.querySelector<HTMLInputElement>(".campo");
// el tipo real es: HTMLInputElement | null
input.value = ""; // ❌ Error: 'input' is possibly 'null'
// PATRÓN 1 (el recomendado): comprobar con un if
if (input) {
input.value = ""; // ✓ dentro del if, TS SABE que no es null
}
// PATRÓN 2: optional chaining (si existe, úsalo; si no, no hace nada)
input?.focus();
// PATRÓN 3: aserción "!" — "confía en mí, no es null". Úsalo con CUIDADO
const boton = document.querySelector(".btn")!; // si te equivocas, revienta en runtimeEl patrón del if es el rey: dentro del bloque, TypeScript "estrecha" el
tipo y sabe que ya no es null. El ! (non-null assertion) es una promesa
tuya al compilador —"esto existe, créeme"— y si te equivocas, vuelve el
error de runtime que TS pretendía evitar. Úsalo solo cuando de VERDAD estés
seguro (un elemento que tú mismo acabas de crear, por ejemplo).
El resultado, corriendo
<div class="caja">
<input id="campo" type="text" placeholder="Escribe y presiona Enter" aria-label="Texto">
<p id="eco" class="eco">Aquí aparecerá tu texto</p>
</div>body { font-family: system-ui; padding: 20px; background: #f8fafc; }
.caja { background: white; border: 1px solid #e2e8f0; border-radius: 12px; padding: 20px; max-width: 320px; display: grid; gap: 12px; }
input { padding: 9px 12px; border: 1px solid #cbd5e1; border-radius: 8px; font-size: 15px; }
.eco { margin: 0; color: #6d28d9; font-weight: 600; font-size: 15px; min-height: 20px; }// este es el JavaScript que el navegador ejecuta: el MISMO código, sin los tipos.
// en tu editor lo escribiste como TS (con HTMLInputElement y el if de null).
const campo = document.querySelector("#campo");
const eco = document.querySelector("#eco");
if (campo && eco) {
campo.addEventListener("keydown", (event) => {
if (event.key === "Enter") {
eco.textContent = campo.value || "(vacío)";
}
});
}Fíjate en el comentario del demo: lo que corre es JavaScript (los tipos se
borraron), pero lo ESCRIBISTE en TypeScript con HTMLInputElement y el
if de null protegiéndote. El editor te avisó de cualquier error antes;
el navegador recibió JS limpio. Esa es la división de trabajo de todo el
módulo.
Escribes const input = document.querySelector('#email') y TS marca error al usar input.value: 'possibly null' y 'value does not exist'. ¿Cómo lo resuelves bien?
Mini-reto
Monta un Vite vanilla-ts (npm create vite@latest) y escribe un pequeño
script tipado: selecciona un input y un botón con sus tipos correctos,
comprueba que no son null, y al hacer clic muestra el valor del input en
un párrafo. Prueba a QUITAR el <HTMLInputElement> y mira el error
aparecer; prueba a quitar el if del null y mira el otro. Provocar los
errores a propósito es la mejor forma de entender qué te está protegiendo.
Qué sigue
Ya tipas elementos. Falta tipar lo que PASA con ellos: los eventos. La
próxima lección cubre MouseEvent, KeyboardEvent, SubmitEvent y el
event.currentTarget — para que event.key, event.target.value y demás
vengan con autocompletado y sin sorpresas.