Mantén tipos fiables en las fronteras de tu aplicación, donde el DOM, la red y el JSON escapan al compilador.
Abrir esta lección en KodokonEl código del mundo real empieza donde terminan los tipos perfectos: el DOM, la red y el JSON devuelven datos que TypeScript no puede adivinar. Primera frontera: el DOM. querySelector devuelve Element | null, un tipo demasiado vago para leer value o llamar a focus().
const input = document.querySelector<HTMLInputElement>(
"#email"
);
if (input) {
input.value = "test@example.com";
input.focus();
}El parámetro de tipo es una promesa que le haces al compilador: es cosa tuya apuntar al selector correcto. Cuando el elemento proviene de un evento, prefiere una comprobación instanceof, que verifica de verdad el tipo en tiempo de ejecución.
const form = document.querySelector("form");
form?.addEventListener("submit", (event) => {
event.preventDefault();
if (event.target instanceof HTMLFormElement) {
console.log(new FormData(event.target));
}
});Segunda frontera: la red. response.json() devuelve Promise<any>, es decir, una puerta abierta a cualquier cosa. El mínimo profesional: declarar el tipo esperado, comprobar response.ok y centralizar la llamada en una función tipada.
interface Post {
id: number;
title: string;
}
async function getPost(id: number): Promise<Post> {
const url = `https://api.example.com/posts/${id}`;
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
return (await response.json()) as Post;
}Ese as Post sigue siendo una declaración de confianza: si la API cambia, nada te avisa en tiempo de ejecución. Para datos críticos, escribe una guardia de tipo que valide la forma real del JSON antes de usarlo.
function isPost(value: unknown): value is Post {
if (typeof value !== "object" || value === null) {
return false;
}
const obj = value as Record<string, unknown>;
return (
typeof obj.id === "number" &&
typeof obj.title === "string"
);
}