Bienvenido al segundo post de la serie TypeScript, si te perdiste el primero, empieza por aquí que te va a hacer falta.
Hoy vamos a meternos en lo que hace que TypeScript sea tan potente: los tipos básicos y, sobre todo, la inferencia de tipos. Cuando acabes este post vas a entender por qué la mitad de las veces ni siquiera necesitas escribir los tipos a mano, TypeScript los adivina solito.

1. Tipado fuerte y estático: las dos reglas de oro
Antes de ver los tipos, necesitas entender dos conceptos que son la base de todo en TypeScript:
Los tipos se verifican en tiempo de compilación, antes de ejecutar nada. Si hay un error de tipos, el código ni compila. Es como un portero de discoteca que no te deja entrar por ir con la camiseta de tu equipo de fútbol favorito.
Si una variable nace siendo un string, no puede mutar a number después. No hay conversiones automáticas raras. Cada tipo se respeta.
¿Qué significa esto en la práctica? Que en TypeScript no puedes hacer estas cositas:
let nombre = 'Domin'; // TypeScript sabe que es string
nombre = 42; // ❌ Error: Type 'number' is not assignable to type 'string'
let edad = 30; // TypeScript sabe que es number
edad = 'treinta'; // ❌ Error: Type 'string' is not assignable to type 'number'
Mientras que en JavaScript, esto se puede hacer porque tu lo vales y porquesi. En TypeScript, el editor te marca el error al momento de escribirlo, como debe ser.
Dato clave: TypeScript es 💡 tipado fuerte Sistema de tipos que no permite conversiones implícitas entre tipos incompatibles. Un string no puede convertirse mágicamente en un number sin una conversión explícita. Más info → y 💡 tipado estático Los tipos se comprueban en tiempo de compilación (antes de ejecutar). A diferencia del tipado dinámico de JavaScript, donde los tipos se resuelven en ejecución. Más info → . JavaScript, en cambio, es débil y dinámico, de ahí el caos.
2. Los tipos básicos: el abecedario de TypeScript
TypeScript tiene unos pocos de tipos primitivos que son la base de todo, los vas a usar frecuentemente:
string — Texto
let nombre: string = 'Domin';
let saludo: string = `Hola, ${nombre}`; // Template literals también
let vacio: string = ''; // String vacío, sigue siendo string
number — Números (todos)
let edad: number = 28;
let precio: number = 19.99; // Decimales incluidos
let negativo: number = -5;
let hex: number = 0xff; // Hexadecimal
let infinito: number = Infinity;
A diferencia de otros lenguajes, TypeScript no distingue entre enteros y decimales, todo es number. Nada de int, float, double… aquí todo es tipo number e ya.
boolean — Verdadero o falso
let activo: boolean = true;
let eliminado: boolean = false;
// ❌ Esto NO es un boolean:
let noEsBoolean: boolean = 'true'; // Error: Type 'string' is not assignable
null y undefined — Los fantasmas
let nada: null = null;
let sinDefinir: undefined = undefined;
Estos dos son especiales. En TypeScript con 💡 strict mode Modo estricto de TypeScript (strictNullChecks). Cuando está activado, null y undefined son tipos independientes y no se pueden asignar a otros tipos sin comprobación explícita.
Más info →
activado (que es lo recomendado), null y undefined son tipos independientes. No puedes meterlos donde no toca:
let nombre: string = null; // ❌ Error con strictNullChecks
let edad: number = undefined; // ❌ Error con strictNullChecks
// ✅ Si quieres permitirlo, úsalo explícitamente:
let nombreOpcional: string | null = null; // Ahora sí
let edadOpcional: number | undefined = undefined; // Ahora sí
Resumen visual de los tipos básicos
| Tipo | Descripción | Ejemplo |
|---|---|---|
| string | Cadenas de texto | "hola", 'mundo', `template` |
| number | Números (enteros y decimales) | 42, 3.14, -7, 0xff |
| boolean | Verdadero o falso | true, false |
| null | Ausencia intencional de valor | null |
| undefined | Variable declarada sin valor | undefined |
3. La magia de la Inferencia de tipos
Hasta ahora hemos escrito los tipos a mano: let nombre: string = "Domin". Pero, ¿y si te digo que la mayoría de las veces no hace falta?
TypeScript tiene un sistema de 💡 inferencia de tipos Capacidad de TypeScript para deducir automáticamente el tipo de una variable, función o expresión a partir del valor que se le asigna o del contexto en que se usa. No necesitas escribir el tipo explícitamente. Más info → tan potente que deduce los tipos por ti mirando qué valor asignas.
// ❌ Esto funciona, pero es REDUNDANTE:
let nombre: string = 'Domin';
let edad: number = 28;
let activo: boolean = true;
// ✅ TypeScript ya lo sabe sin que se lo digas:
let nombre = 'Domin'; // Tipo inferido: string
let edad = 28; // Tipo inferido: number
let activo = true; // Tipo inferido: boolean
TypeScript mira el valor 'Domin' y piensa: “Esto es un string, así que nombre es un string. No necesitas decírmelo, nene”. Y lo mejor es que el autocompletado y la protección de tipos funcionan exactamente igual.
Pruébalo tú mismo
¿Lo ves? En ningún momento hemos escrito : string y sin embargo TypeScript sabe que mensaje es un string. Si intentas asignarle un número, te frena en seco. Y .toUpperCase() aparece en el autocompletado porque sabe que es un string. Atención que eso es la inferencia que mencionamos en el título!
La inferencia también funciona con funciones
TypeScript no solo infiere variables, también infiere lo que devuelve una función:
// TypeScript infiere que el retorno es number
function sumar(a: number, b: number) {
return a + b;
}
const resultado = sumar(5, 3); // tipo inferido: number
resultado.toFixed(2); // ✅ Autocompletado de number
// TypeScript infiere que el retorno es string
function saludar(nombre: string) {
return `Hola, ${nombre}!`;
}
const saludo = saludar('Domin'); // tipo inferido: string
saludo.toUpperCase(); // ✅ Autocompletado de string
Fíjate que no hemos puesto : number ni : string en el retorno de las funciones y TypeScript lo deduce solo mirando qué hay dentro del return. Esto es la inferencia.
¿Cuándo escribir el tipo y cuándo dejar que infiera?
Esto es lo que me hubiera gustado saber a mi desde el principio:
- Declaras una variable sin asignarle valor: let nombre: string;
- El tipo no es obvio al leer el código
- Quieres documentar una función pública (parámetros)
- Trabajas con tipos complejos (objetos, arrays de objetos)
- El valor puede ser de varios tipos (string | number)
- Asignas un valor directamente: let x = 42
- El retorno de la función es obvio por el código
- Es una variable local con un valor simple
- Estás haciendo una operación que el tipo es evidente
Si TypeScript puede inferirlo, no lo escribas. Si no puede, ayúdalo.
4. Inferencia en objetos y arrays
La inferencia no se queda en tipos simples, también funciona con estructuras más complejas:
Objetos
// TypeScript infiere toda la estructura del objeto
const usuario = {
nombre: 'Domin',
edad: 28,
activo: true,
};
// Tipo inferido: { nombre: string; edad: number; activo: boolean }
usuario.nombre.toUpperCase(); // ✅ Sabe que es string
usuario.edad.toFixed(2); // ✅ Sabe que es number
usuario.activo = 'sí'; // ❌ Error: Type 'string' is not assignable to type 'boolean'
usuario.email; // ❌ Error: Property 'email' does not exist
Arrays
// TypeScript infiere el tipo del array por sus elementos
const numeros = [1, 2, 3]; // Tipo: number[]
const nombres = ['Ana', 'Luis']; // Tipo: string[]
const mixto = [1, 'dos', true]; // Tipo: (number | string | boolean)[]
numeros.push('cuatro'); // ❌ Error: Argument of type 'string'
// is not assignable to parameter of type 'number'
nombres.map(n => n.toUpperCase()); // ✅ Sabe que cada 'n' es string
Ojito con el array mixto: TypeScript infiere un tipo unión tipo unión Un tipo que puede ser uno de varios tipos posibles, representado con el operador | (pipe). Ejemplo: string | number significa que puede ser string o number. (number | string | boolean)[]. Es listo, pero intenta evitar arrays con tipos mezclados… eso es JavaScript caótico, no TypeScript limpio.
El autocompletado se vuelve mágico
Cuando TypeScript infiere el tipo de un objeto, tu editor sabe exactamente qué propiedades tiene, mira este ejemplo:
const producto = {
nombre: 'Teclado MX Keys',
precio: 119.99,
enStock: true,
tags: ['periférico', 'logitech'],
};
// Al escribir "producto." tu editor te sugiere:
// → nombre (string)
// → precio (number)
// → enStock (boolean)
// → tags (string[])
// Y encadenas métodos con autocompletado completo:
producto.tags.filter(t => t.startsWith('log')); // ✅
producto.precio.toFixed(2); // ✅
producto.nombre.split(' '); // ✅
Esto sin haber escrito ni un solo tipo explícito, todo inferido. Si vienes de trabajar con JavaScript vanilla donde el autocompletado es un a ver si cuela, esto es canela fina nene!
5. El peligro del any: el villano de TypeScript
Ahora vienen las malas noticias, existe un tipo en TypeScript que lo destruye todo: 💡 any Tipo especial que desactiva completamente la comprobación de tipos. Una variable con tipo any puede ser cualquier cosa y TypeScript no la verificará. Es el equivalente a volver a JavaScript puro. Más info → .
let cosa: any = 'hola';
cosa = 42; // ✅ Sin error
cosa = true; // ✅ Sin error
cosa = { a: 1 }; // ✅ Sin error
cosa.metodoQueNoExiste(); // ✅ Sin error... PERO PETARÁ EN RUNTIME 💥
¿Ves el problema? Con any le estás diciendo a TypeScript: “Calla, tú no sabes nada. Yo me encargo.” Y TypeScript se calla y no te avisa de errores. No te da autocompletado. No te protege de nada. Estás de vuelta en JavaScript puro, pero con la falsa sensación de que estás “usando TypeScript”.
✅ PROS
❌ CONTRAS
El efecto dominó del any
Lo peor del any no es usarlo una vez, es que se contagia:
function obtenerDatos(): any {
return { nombre: 'Domin', edad: 28 };
}
const datos = obtenerDatos(); // tipo: any
const nombre = datos.nombre; // tipo: any (¡se ha propagado!)
const largo = nombre.length; // tipo: any (¡otra vez!)
// Todo el código que depende de esta función pierde tipos
// Es como un virus que infecta toda la cadena
Una sola función con any en el retorno contamina todo lo que la toque. El any es una bola de nieve que crece y crece hasta que tu proyecto TypeScript es básicamente JavaScript disfrazado.
Por eso, si ves un código en TypeScript y aparece any, preocupate!
La alternativa segura: unknown
Si necesitas un tipo genérico pero sin perder la seguridad, existe 💡 unknown Tipo seguro que representa 'cualquier valor' pero que OBLIGA a comprobar el tipo antes de usarlo. A diferencia de any, no permite operaciones sin verificación previa. Más info → :
let dato: unknown = 'hola';
// ❌ Con unknown NO puedes hacer esto directamente:
dato.toUpperCase(); // Error: 'dato' is of type 'unknown'
// ✅ Primero tienes que VERIFICAR el tipo:
if (typeof dato === 'string') {
dato.toUpperCase(); // Ahora sí, TypeScript sabe que es string
}
if (typeof dato === 'number') {
dato.toFixed(2); // Aquí sabe que es number
}
La diferencia es bastante considerable porque unknown te obliga a comprobar qué tipo es antes de usarlo. Es como decir: “No sé qué es esto, pero antes de tocarlo voy a asegurarme”. Mientras que any es: “Me da igual qué sea, me llamo Ralph”.
| Característica | any | unknown |
|---|---|---|
| Asignar cualquier valor | ✅ Sí | ✅ Sí |
| Usar sin comprobar tipo | ✅ Sí (peligroso) | ❌ No (te obliga a verificar) |
| Autocompletado del editor | ❌ Ninguno | ✅ Tras la verificación |
| Seguridad de tipos | ❌ Nula | ✅ Completa |
| ¿Cuándo usarlo? | Casi nunca (migración JS→TS) | Datos de origen desconocido (APIs, JSON) |
6. any vs unknown en la práctica
Veamos un caso real, imaginando que recibes datos de una API y no sabes qué estructura tienen:
// ❌ MAL: con any
async function fetchUsuarioMal(): Promise<any> {
const res = await fetch('/api/usuario');
return res.json(); // Devuelve any... adiós tipos
}
const user = await fetchUsuarioMal();
// user.nombre → any (sin autocompletado, sin protección)
// user.loquesea → any (ni se queja)
// ✅ BIEN: con unknown + validación
async function fetchUsuarioBien(): Promise<unknown> {
const res = await fetch('/api/usuario');
return res.json();
}
const data = await fetchUsuarioBien();
// Antes de usar, verificamos:
if (
typeof data === 'object' &&
data !== null &&
'nombre' in data &&
'edad' in data
) {
// Ahora TypeScript sabe que tiene nombre y edad
console.log(data.nombre, data.edad);
}
¿Cuál es más código? La versión con unknown. ¿Cuál es más segura? También la de unknown. Y ya sabes lo que decimos: más código ahora, menos bugs después. Ta enterao o no.
7. Errores comunes de novato con los tipos
Estos son los errores que todo el mundo comete cuando empieza con TypeScript. Si los evitas desde el principio, ya vas por delante del 80% de la gente:
let x: number = 5; — Redundante. TypeScript ya sabe que 5 es number. Simplifica: let x = 5;
El clásico const data: any = ... para quitarse el error de encima. No. Busca el tipo correcto o usa unknown.
Sin strict mode, null y undefined se cuelan en todos los tipos sin avisar. Siempre "strict": true en tsconfig.
Si TypeScript infiere un tipo, úsalo. Escribe variable. y deja que el editor te sugiera. Es la mayor ventaja de TS.
8. Checklist: domina los tipos básicos
Conclusión
Los tipos básicos de TypeScript son sencillos: string, number, boolean, null, undefined, nada raro. Pero lo que de verdad hace especial a TypeScript es la inferencia porque no necesitas escribir tipos a mano el 90% del tiempo. TypeScript los deduce solo y te da autocompletado, protección contra errores y documentación automática gratis.
Piensa que cada vez que usas any, un gatito o perrito es sacrificado. any es el backdoor que destruye todo lo que TypeScript te ofrece. Si necesitas flexibilidad, usa unknown y verifica el tipo antes de usarlo.
En el próximo post veremos los tipos avanzados: type vs interface, tipos unión, intersección, tipos literales y mucho más. Ahí es donde TypeScript empieza a petarlo más aún.
EA, nos vemos en los bares 🍻
Pon a prueba lo aprendido
1. ¿Qué significa que TypeScript tiene tipado fuerte?
2. ¿Cuál es el tipo inferido de: let precio = 19.99?
3. ¿Qué pasa si haces let nombre = 'Domin' y luego nombre = 42?
4. ¿Cuál es el problema principal de usar any?
5. ¿Qué diferencia hay entre any y unknown?
6. ¿Cuándo deberías escribir el tipo explícitamente en lugar de dejar que TypeScript lo infiera?