Tema
Tipos básicos
Los tipos son el vocabulario de TypeScript. En esta lección aprenderás a describir los datos más comunes: textos, números, listas, valores que pueden faltar y conjuntos de opciones fijas. Con esto ya podrás tipar el 80% del código que escribes a diario.
Concepto
Un tipo es un conjunto de valores permitidos. number es el conjunto de todos los números; boolean solo tiene dos valores (true y false); el tipo literal 'admin' tiene un único valor. Cuando anotas una variable, le dices a TS: "aquí solo puede haber valores de este conjunto".
La sintaxis es siempre la misma: dos puntos después del nombre.
ts
let nombre: string = 'Ana';Primitivos: string, number, boolean
ts
const producto: string = 'Audífonos';
const precio: number = 899.5; // enteros y decimales son number
const enOferta: boolean = false;
// Plantillas de texto también son string
const etiqueta: string = `${producto} - $${precio}`;
// Otros primitivos menos comunes
const grande: bigint = 9007199254740993n;
const id: symbol = Symbol('id');Error común
Usa los tipos en minúscula: string, number, boolean. Los que empiezan con mayúscula (String, Number, Boolean) son los objetos envoltorio de JavaScript y casi nunca los quieres.
Inferencia de tipos
No necesitas anotar todo. TypeScript infiere el tipo a partir del valor inicial:
ts
let ciudad = 'Monterrey'; // TS infiere: string
let edad = 30; // number
let activo = true; // boolean
ciudad = 42;
// ❌ Type 'number' is not assignable to type 'string'.
// Con const, TS infiere el tipo más preciso posible: el literal
const rol = 'admin'; // tipo: 'admin' (no solo string)Pasa el cursor sobre una variable en VS Code y verás el tipo inferido.
Buena práctica
Deja que TS infiera las variables locales y anota los parámetros de funciones, los retornos de funciones públicas y los datos que vienen de fuera (APIs, formularios). Anotar const x: number = 5 es ruido.
Arrays
Dos sintaxis equivalentes:
ts
const precios: number[] = [100, 250, 80];
const nombres: Array<string> = ['Ana', 'Luis'];
precios.push('gratis');
// ❌ Argument of type 'string' is not assignable to parameter of type 'number'.
// Arrays de objetos
const usuarios: { nombre: string; edad: number }[] = [
{ nombre: 'Ana', edad: 28 },
{ nombre: 'Luis', edad: 34 }
];
// Arrays de solo lectura: no se pueden modificar
const dias: readonly string[] = ['lun', 'mar', 'mié'];
dias.push('jue');
// ❌ Property 'push' does not exist on type 'readonly string[]'.La forma number[] es la más común; Array<number> usa la sintaxis de genéricos que verás en Genéricos.
Tuplas
Una tupla es un array con longitud fija y un tipo por posición. Útil para pares de valores:
ts
// [latitud, longitud]
const coordenadas: [number, number] = [20.96, -89.62];
// [nombre, edad, activo]
const fila: [string, number, boolean] = ['Ana', 28, true];
// Tuplas con nombres (solo documentan, mejoran el autocompletado)
type Rango = [inicio: number, fin: number];
const horario: Rango = [9, 18];
// Así devuelve React su useState: una tupla [valor, función]
function usarContador(): [number, () => void] {
let valor = 0;
return [valor, () => { valor++; }];
}
const [conteo, incrementar] = usarContador();Uniones
Una unión dice "puede ser esto o aquello". Se escribe con la barra vertical:
ts
let id: string | number;
id = 101; // ✅
id = 'A-101'; // ✅
id = true; // ❌
// Muy común: un valor o null
let usuarioActual: string | null = null;
usuarioActual = 'ana@correo.com';Con una unión solo puedes usar lo que es común a todos los miembros. Para usar algo específico tienes que comprobar el tipo primero:
ts
function formatearId(id: string | number): string {
// id.toUpperCase(); ❌ number no tiene toUpperCase
if (typeof id === 'string') {
return id.toUpperCase(); // aquí TS sabe que es string
}
return id.toString().padStart(5, '0'); // aquí es number
}A esto se le llama narrowing (estrechamiento) y tiene su propia lección: Narrowing.
Tipos literales
Un tipo literal es un valor exacto. Combinados con uniones, crean listas de opciones válidas:
ts
type Estado = 'pendiente' | 'enviado' | 'entregado' | 'cancelado';
type Talla = 'S' | 'M' | 'L' | 'XL';
type Dado = 1 | 2 | 3 | 4 | 5 | 6;
let estadoPedido: Estado = 'pendiente';
estadoPedido = 'enviado'; // ✅
estadoPedido = 'perdido';
// ❌ Type '"perdido"' is not assignable to type 'Estado'.
function cambiarTema(tema: 'claro' | 'oscuro') {
document.body.dataset.tema = tema;
}
cambiarTema('oscuro'); // ✅ y el editor te autocompleta las opcionesEsto es oro en proyectos reales: estados de pedidos, roles de usuarios, variantes de botones, métodos HTTP.
null y undefined con strict
Con strict: true, null y undefined son tipos propios. Un string no puede ser null a menos que lo digas:
ts
let nombre: string = null;
// ❌ Type 'null' is not assignable to type 'string'.
let apodo: string | null = null; // ✅ explícito
let telefono: string | undefined; // ✅ puede no tener valor
// TS te obliga a comprobar antes de usar
function longitud(texto: string | null): number {
// return texto.length; ❌ 'texto' is possibly 'null'.
return texto?.length ?? 0; // ✅
}Esto elimina los famosos "Cannot read properties of null".
any vs unknown
Ambos aceptan cualquier valor, pero se comportan de forma opuesta:
anyapaga TypeScript. Puedes hacer lo que quieras con él y TS no te avisa de nada.unknownes la versión segura: acepta cualquier cosa, pero no te deja usarlo hasta que compruebes qué es.
ts
let a: any = 'hola';
a.metodoQueNoExiste(); // TS no se queja... 💥 explota al ejecutar
a = 5;
a.toUpperCase(); // tampoco se queja
let u: unknown = 'hola';
u.toUpperCase();
// ❌ 'u' is of type 'unknown'.
if (typeof u === 'string') {
u.toUpperCase(); // ✅ ahora sí: comprobaste que es string
}
// Caso real: JSON.parse devuelve any; tú lo tratas como unknown
const datos: unknown = JSON.parse('{"precio": 100}');Buena práctica
Evita any. Cuando no sepas qué tipo llega (una API, JSON.parse, un catch), usa unknown y valida. Es como recibir un paquete sin etiqueta: lo revisas antes de usarlo.
void y never
void: la función no devuelve nada útil.never: la función nunca termina (lanza un error o es un bucle infinito), o un valor que no puede existir.
ts
function registrar(mensaje: string): void {
console.log(`[LOG] ${mensaje}`);
}
function fallar(mensaje: string): never {
throw new Error(mensaje);
}
// never también aparece cuando una unión se queda sin opciones
function describir(x: string | number) {
if (typeof x === 'string') return 'texto';
if (typeof x === 'number') return 'número';
const imposible: never = x; // aquí x ya no puede ser nada
return imposible;
}Verás never en acción para revisar exhaustividad en Narrowing.
Enums vs uniones de literales
TypeScript tiene enum, una forma de nombrar un conjunto de constantes:
ts
enum EstadoPedido {
Pendiente = 'pendiente',
Enviado = 'enviado',
Entregado = 'entregado'
}
let e: EstadoPedido = EstadoPedido.Enviado;
console.log(e); // 'enviado'Pero los enums tienen detalles incómodos: generan código JavaScript extra (son de las pocas cosas de TS que no se borran), los numéricos aceptan números arbitrarios y no funcionan con el modo de Node que solo quita tipos. Hoy la mayoría de los equipos prefieren uniones de literales:
ts
type EstadoPedido = 'pendiente' | 'enviado' | 'entregado';
let estado: EstadoPedido = 'enviado'; // simple, sin código extraSi además necesitas la lista de valores en tiempo de ejecución (para un <select>, por ejemplo), usa as const.
as const
as const le dice a TS: "trata este valor como constante y lo más preciso posible". Los arrays se vuelven tuplas de solo lectura y los strings se vuelven literales:
ts
const colores = ['rojo', 'verde', 'azul'];
// tipo: string[]
const coloresFijos = ['rojo', 'verde', 'azul'] as const;
// tipo: readonly ['rojo', 'verde', 'azul']
// Derivar una unión desde el array: ¡una sola fuente de verdad!
const ESTADOS = ['pendiente', 'enviado', 'entregado'] as const;
type Estado = (typeof ESTADOS)[number];
// 'pendiente' | 'enviado' | 'entregado'
// Ahora puedes recorrer los valores y también usarlos como tipo
ESTADOS.forEach((e) => console.log(e));
// Objetos de configuración
const CONFIG = {
apiUrl: 'https://api.mitienda.mx',
reintentos: 3
} as const;
// CONFIG.reintentos = 5; ❌ es readonlyEl truco (typeof ESTADOS)[number] significa "el tipo de cualquier elemento del array". Lo usarás mucho.
Ejercicios
- Declara (con el tipo adecuado) un array de precios, una tupla
[nombre, cantidad]y una variable que pueda serstringonull. - Crea un tipo
MetodoHttpcon'GET','POST','PUT'y'DELETE', y una funciónpeticion(url: string, metodo: MetodoHttp)que devuelva un texto como"GET /api/usuarios". - Escribe una función
aMayusculas(valor: unknown): stringque devuelva el texto en mayúsculas si es string, y''en cualquier otro caso. - Con
as const, crea un arrayROLEScon'admin','editor'y'lector', y deriva de él el tipoRol.
Ver solución
ts
// 1
const precios: number[] = [120, 80, 45];
const linea: [string, number] = ['Café', 2];
let cupon: string | null = null;
// 2
type MetodoHttp = 'GET' | 'POST' | 'PUT' | 'DELETE';
function peticion(url: string, metodo: MetodoHttp): string {
return `${metodo} ${url}`;
}
peticion('/api/usuarios', 'GET'); // 'GET /api/usuarios'
// 3
function aMayusculas(valor: unknown): string {
if (typeof valor === 'string') {
return valor.toUpperCase();
}
return '';
}
// 4
const ROLES = ['admin', 'editor', 'lector'] as const;
type Rol = (typeof ROLES)[number]; // 'admin' | 'editor' | 'lector'Resumen
- Primitivos:
string,number,boolean(en minúscula). - TS infiere tipos; anota sobre todo parámetros y datos externos.
- Arrays:
number[]oArray<number>; tuplas:[number, number]. - Uniones (
A | B) y literales crean opciones precisas:'claro' | 'oscuro'. - Con
strict,nullyundefineddeben declararse explícitamente. unknownen lugar deany: obliga a comprobar antes de usar.void= no devuelve nada;never= nunca ocurre.- Prefiere uniones de literales sobre
enum; usaas constpara derivar tipos desde valores.
Siguiente: Interfaces y types