learning-front

Nivel 5 · TypeScript: JavaScript con red de seguridad

Tipos básicos y anotaciones

string, number, boolean, arrays y el inferidor: qué escribir y qué dejar que TypeScript adivine por ti.

TypeScript no es un lenguaje nuevo: es JavaScript con una red de seguridad. Escribes el mismo código que ya conoces del nivel 2 y 3, pero le añades tipos que describen qué clase de dato es cada cosa. A cambio, el editor te avisa de los errores mientras escribes, en lugar de descubrirlos cuando la página ya está rota delante de un usuario.

Un ejemplo de los que duelen en JavaScript puro:

javascript
// JavaScript: nadie te frena.
// partidas llega como texto (typo, un value de un input...).
const partidas = "120";
// 78 / "120" "funciona" por coerción y devuelve un número raro: el bug no salta
// aquí, sino más tarde, cuando el winrate sale mal en producción.
const ratio = 78 / partidas;

Con TypeScript, ese descuido se marca en rojo antes de ejecutar. Veamos cómo.

Anotar un tipo#

Una anotación de tipo se escribe tras dos puntos, justo después del nombre: le dices a TypeScript qué esperas y él vigila que se cumpla. Los tres tipos primitivos que más usarás:

typescript
// string: texto entre comillas.
const nombre: string = "Tracer";
// number: cualquier número, entero o decimal.
const partidas: number = 120;
// boolean: solo true o false.
const activa: boolean = true;

Si rompes la promesa, TypeScript no te deja pasar:

typescript
// Error TS2322: Type 'string' is not assignable to type 'number'.
// Has dicho "esto es un number" y le has dado un texto.
const mal: number = "120";

Pruébalo: el editor de abajo type-chequea de verdad. Descomenta la última línea y mira cómo aparece el error en el panel “Problemas” sin ejecutar nada.

Anotar funciones#

Una función se anota en dos sitios: cada parámetro lleva su tipo, y el tipo que devuelve va tras el paréntesis de cierre, también con dos puntos. Así TypeScript vigila a la vez lo que entra y lo que sale.

typescript
// victorias: number y partidas: number anotan los parámetros (lo que entra).
// El ": number" tras el paréntesis anota el retorno (lo que sale).
function winrate(victorias: number, partidas: number): number {
  // Dentro, victorias y partidas ya son number: la cuenta es segura.
  return (victorias / partidas) * 100;
}

Los parámetros hay que anotarlos: al declararlos no tienen valor, así que TypeScript no puede adivinar su tipo. El retorno, en cambio, es opcional —TypeScript también lo deduce del return—, pero anotarlo es un contrato: si un día cambias el cuerpo y devuelves otra cosa por error, el rojo salta en la propia función, no tres llamadas más allá donde se usa el resultado.

Dejar que TypeScript infiera#

No hace falta anotar todo. Si declaras una variable con un valor, TypeScript deduce el tipo solo: es la inferencia. Anotar de más es ruido.

typescript
// TypeScript ya sabe que esto es string: no hace falta el ": string".
const nombre = "Mercy";
// Y que esto es number.
const partidas = 200;

El tipo sigue ahí aunque no lo escribas: si intentas usar la variable como otra cosa, salta el error igual. La regla práctica: anota cuando TypeScript no puede adivinar (los parámetros de una función, o una variable sin valor inicial) y deja que infiera el resto.

Arrays tipados#

Un array tipado declara de qué son sus elementos: el tipo seguido de [].

typescript
// number[]: una lista de números.
const porSemana: number[] = [12, 8, 15, 20];
// string[]: una lista de textos.
const roles: string[] = ["Daño", "Tanque", "Apoyo"];

A cambio, TypeScript vigila que no metas un intruso: porSemana.push("nueve") se marca en rojo, porque "nueve" no es un number. Y como sabe que porSemana son números, dentro de un reduce infiere que el acumulador también lo es, sin que anotes nada.

any y unknown: los dos comodines#

A veces no sabes (todavía) qué tipo tiene un valor: llega de un formulario, de una API, de fuera. Hay dos formas de decir “esto puede ser cualquier cosa”, y se comportan al revés:

  • any apaga la comprobación. TypeScript deja de mirar ese valor y te deja hacer lo que sea con él. Es cómodo y peligroso: renuncias justo a la red de seguridad.
  • unknown también acepta cualquier valor, pero no te deja usarlo hasta que compruebes de qué se trata. Es la puerta de entrada segura para datos externos.
typescript
// any: TypeScript no se queja, aunque la operación no tenga sentido.
const sucio: any = "no soy un número";
// unknown: TypeScript te frena hasta que compruebes qué es.
const seguro: unknown = "no soy un número";
// Esto da error con unknown (y por eso te protege):
// const doble = seguro * 2;

Para usar un unknown, primero compruebas qué es. Con typeof, dentro del if TypeScript ya lo trata como ese tipo y te deja operar sin error:

typescript
// dato podría ser cualquier cosa: es unknown.
const dato: unknown = 42;
// typeof comprueba el tipo en tiempo de ejecución.
if (typeof dato === "number") {
  // Dentro del if, TypeScript ya sabe que dato es number: operar es seguro.
  console.log(dato * 2);
}

La regla: evita any; cuando un dato sea de verdad desconocido, usa unknown y compruébalo antes de tocarlo. Esa comprobación es solo el aperitivo: el repertorio completo para estrechar tipos lo verás en el capítulo de narrowing.

Comprueba lo que sabes#

Pregunta 1 de 5

¿Para qué sirve una anotación de tipo como `const n: number`?

Tu turno#

Tienes el motor de estadísticas del Team Builder a medio tipar. Complétalo en el editor: el panel “Problemas” te guía, y al darle a “Ejecutar” ves el resumen. Cuando lo tengas (o si te atascas), despliega las soluciones y fíjate en el salto de un nivel al siguiente.

Ejercicio · en esta página

Tipa el motor de estadísticas

Partes de un fichero con lógica que ya conoces (winrate, formato y un reduce) pero SIN anotar. Añade las anotaciones que faltan hasta que el panel "Problemas" quede vacío, y elige bien el tipo del dato externo.

Paso 1: Que funcione

  • El panel "Problemas" queda vacío: no hay errores de tipo.
  • Las funciones tienen sus parámetros anotados.
  • Al pulsar "Ejecutar" se ve el resumen en la consola.
Ver soluciones
// Anotamos TODO de forma explícita: lo más directo cuando empiezas con tipos.

// Parámetros y retorno anotados a mano.
function winrate(victorias: number, partidas: number): number {
  return victorias / partidas;
}

// Igual: parámetro y retorno explícitos.
function formatearPorcentaje(ratio: number): string {
  return (ratio * 100).toFixed(1) + "%";
}

// Cada variable con su tipo escrito a mano.
const nombre: string = "Tracer";
const partidas: number = 120;
const victorias: number = 78;

// Array de números.
const porSemana: number[] = [12, 8, 15, 20];

// Lo dejamos como any para que "compile y ya": funciona, pero apaga la seguridad.
let datoExterno: any;

// reduce sobre el array de números.
const totalSemanas: number = porSemana.reduce(
  (suma: number, n: number) => suma + n,
  0,
);

// Pinta el resumen.
console.log(`Jugadora: ${nombre}`);
console.log(`Winrate: ${formatearPorcentaje(winrate(victorias, partidas))}`);
console.log(`Partidas últimas semanas: ${totalSemanas}`);

Por qué este nivel

  • Anota TODO de forma explícita, también las variables que TypeScript ya podría inferir: es la traducción literal de "ponle tipo a cada cosa".
  • Compila sin errores y funciona, que es el primer requisito.
  • Su límite: para "salir del paso" marca el dato externo como any, que apaga la red de seguridad justo donde más falta hace (datos de fuera).