TypeScript es JavaScript con anotaciones de tipo. Un compilador revisa el código antes de ejecutarlo y te avisa si, por ejemplo, pasas un texto donde se esperaba un número. Después genera JavaScript normal, así que funciona en cualquier navegador o en Node.js. Conviene saber algo de JavaScript antes de empezar.
Aplicaciones web grandes
Servidores con Node.js
Bibliotecas y herramientas
Para empezar: Prueba los ejemplos en el TypeScript Playground oficial, en el navegador, sin instalar nada.
Nivel 1 Básico
1
Lección 1 · Básico
Qué es TypeScript y por qué usarlo
En JavaScript, un error como sumar un número a un texto solo se descubre al ejecutar el programa. TypeScript añade tipos y un compilador (tsc) que revisa el código antes y te marca esos fallos en el editor.
El resultado es JavaScript corriente: los tipos se eliminan al compilar. Los archivos llevan la extensión .ts.
Ejemplo práctico
function doble(n: number) {
return n * 2;
}
doble(4); // 8
doble("cuatro"); // Error del compilador:
// Argument of type 'string' is not assignable to parameter of type 'number'.
Ejercicio
¿Cuándo detecta TypeScript un error de tipos?
Ver la solución
Respuesta: Al compilar, antes de ejecutar. El compilador revisa los tipos antes de generar el JavaScript, así que el fallo aparece mientras programas.
2
Lección 2 · Básico
Tipos básicos e inferencia
Los tipos básicos son string, number y boolean; las listas se escriben number[] o string[]. Se anotan con dos puntos tras el nombre.
No hace falta anotarlo todo: si escribes let edad = 30, TypeScript deduce que es number (inferencia de tipos).
Ejemplo práctico
let nombre: string = "Ana";
let edad = 30; // inferido: number
const notas: number[] = [7, 8.5, 9];
edad = "treinta";
// Error: Type 'string' is not assignable to type 'number'.
Ejercicio
Completa el tipo de una lista de textos.
const nombres: [] = ["Ana", "Luis"];
Ver la solución
Respuesta:string. Una lista de textos es string[].
3
Lección 3 · Básico
Funciones con tipos
En una función se anotan los parámetros y, opcionalmente, lo que devuelve, después del paréntesis. Un ? tras el nombre hace que el parámetro sea opcional.
Así, quien use la función sabe exactamente qué tiene que pasarle, y el editor lo autocompleta.
Con un valor por defecto, TypeScript ya deduce que moneda es string, así que moneda = "€" también es válido. El tipo de retorno va después de los paréntesis.
Con un valor por defecto, TypeScript ya deduce que moneda es string, así que moneda = "€" también es válido. El tipo de retorno va después de los paréntesis.
4
Lección 4 · Básico
Interfaces: la forma de un objeto
Una interfaz describe qué propiedades tiene un objeto y de qué tipo. Si falta una obligatoria o sobra una desconocida al crear el objeto, el compilador avisa.
Con type puedes definir formas muy parecidas; las interfaces se suelen preferir para objetos.
Completa la palabra que declara la forma de un objeto.
Usuario {
nombre: string;
}
Ver la solución
Respuesta:interface. interface Nombre { … } define las propiedades que debe tener un objeto.
Escribe el código
Declara la interfaz Producto con nombre (texto), precio (número) y stock (número, opcional). Después crea una constante teclado de tipo Producto con nombre «Teclado» y precio 35, sin stock.
Tab escribe una sangría; para salir del editor con el teclado, pulsa Esc y luego Tab. Ctrl + Intro corrige.
El ? hace que la propiedad sea opcional: el objeto puede no tenerla. Si stock no fuera opcional, TypeScript daría un error al crear el teclado sin él.
Nivel 2 Intermedio
5
Lección 5 · Intermedio
Uniones y estrechamiento
Un tipo unión admite varias opciones: string | number. También sirve con valores concretos, como "claro" | "oscuro".
Para usar algo específico de una de las opciones, compruébalo antes (con typeof, por ejemplo); TypeScript entiende que dentro de ese if el tipo ya es el concreto. Es el estrechamiento de tipos.
Ejemplo práctico
type Tema = "claro" | "oscuro";
let tema: Tema = "oscuro";
function formatear(valor: string | number): string {
if (typeof valor === "number") {
return valor.toFixed(2); // aquí valor es number
}
return valor.trim(); // aquí valor es string
}
Ejercicio
Con type Tema = "claro" | "oscuro", ¿qué asignación da error?
Ver la solución
Respuesta: tema = "azul". Solo se admiten los dos valores de la unión; "azul" no es uno de ellos.
Encuentra los errores
normalizar recibe un texto o un número y siempre debería devolver un número. TypeScript marca 2 errores: encuéntralos.
Los errores
Línea 4:typeof devuelve "string" para los textos, nunca "text"; TypeScript avisa de que esa comparación no puede cumplirse. Corrección: if (typeof entrada === "string") {
Línea 7: Aquí entrada ya solo puede ser un número, que no tiene trim(). Basta con devolverlo. Corrección: return entrada;
Tras el if con typeof, TypeScript «estrecha» el tipo: dentro es string y después, number. Por eso puede avisar de los dos errores antes de ejecutar nada.
Ver la solución
Línea 4:typeof devuelve "string" para los textos, nunca "text"; TypeScript avisa de que esa comparación no puede cumplirse. Corrección: if (typeof entrada === "string") {
Línea 7: Aquí entrada ya solo puede ser un número, que no tiene trim(). Basta con devolverlo. Corrección: return entrada;
6
Lección 6 · Intermedio
Genéricos
Un genérico es un tipo que se rellena al usarlo, escrito entre < y >. Permite escribir una función que sirve para listas de cualquier tipo sin perder la información de cuál es.
Array<number> es lo mismo que number[], y Promise<string> es una promesa que dará un texto.
Ejemplo práctico
function primero<T>(lista: T[]): T | undefined {
return lista[0];
}
const n = primero([10, 20]); // n: number | undefined
const s = primero(["a", "b"]); // s: string | undefined
Ejercicio
En primero<T>(lista: T[]), ¿qué es T?
Ver la solución
Respuesta: Un tipo que se decide al llamar a la función. T es un parámetro de tipo: se sustituye por el tipo real de la lista que pases.
Encuentra los errores
ultimo debería devolver el último elemento de cualquier lista, y n debería valer 3. Hay 2 errores de tipos.
Los errores
Línea 1: El parámetro es una lista de elementos de tipo T, así que su tipo es T[]; con T a secas no tiene length ni posiciones. Corrección: function ultimo<T>(lista: T[]): T | undefined {
Línea 5: Se fuerza T a string, pero la lista tiene números. Lo más sencillo es no indicarlo y dejar que TypeScript lo deduzca (number). Corrección: const n: number | undefined = ultimo([1, 2, 3]);
En casi todas las llamadas a funciones genéricas no hace falta escribir <tipo>: TypeScript lo deduce de los argumentos.
Ver la solución
Línea 1: El parámetro es una lista de elementos de tipo T, así que su tipo es T[]; con T a secas no tiene length ni posiciones. Corrección: function ultimo<T>(lista: T[]): T | undefined {
Línea 5: Se fuerza T a string, pero la lista tiene números. Lo más sencillo es no indicarlo y dejar que TypeScript lo deduzca (number). Corrección: const n: number | undefined = ultimo([1, 2, 3]);
7
Lección 7 · Intermedio
null y undefined sin sustos
Con la opción strict activada, un valor que puede faltar se escribe string | null y TypeScript te obliga a comprobarlo antes de usarlo. Así desaparecen muchos errores de «undefined is not a function».
El encadenamiento opcional ?. devuelve undefined si algo falta, en lugar de fallar, y ?? da un valor por defecto solo cuando el valor es null o undefined.
Completa para usar «Anónimo» si el nombre es null o undefined.
const visible = nombre "Anónimo";
Ver la solución
Respuesta:??. ?? devuelve la parte derecha solo si la izquierda es null o undefined (a diferencia de ||, no cambia un texto vacío ni un 0).
Nivel 3 Avanzado
8
Lección 8 · Avanzado
Clases con modificadores
TypeScript añade a las clases de JavaScript modificadores como private (solo accesible dentro de la clase) y readonly (no se puede cambiar tras crearla).
Con implements una clase se compromete a cumplir una interfaz, y el compilador comprueba que tiene todo lo necesario.
Respuesta: Cambiar la propiedad después de crear el objeto. Una propiedad readonly se asigna al crear el objeto y ya no puede reasignarse.
9
Lección 9 · Avanzado
Tipos utilitarios
TypeScript incluye tipos que transforman otros: Partial<T> hace opcionales todas las propiedades, Pick<T, …> se queda con algunas, Readonly<T> impide modificarlas y Record<K, V> describe un objeto con claves de un tipo y valores de otro.
Te ahorran repetir definiciones casi iguales, por ejemplo para los datos de un formulario de edición.
Ejemplo práctico
interface Producto { id: number; nombre: string; precio: number }
type CambiosProducto = Partial<Producto>; // todo opcional
type Resumen = Pick<Producto, "id" | "nombre">;
const stock: Record<string, number> = { teclado: 10, raton: 0 };
function actualizar(p: Producto, cambios: CambiosProducto): Producto {
return { ...p, ...cambios };
}
Ejercicio
¿Qué tipo hace opcionales todas las propiedades de otro?
Ver la solución
Respuesta: Partial. Partial<T> crea un tipo igual a T pero con todas sus propiedades opcionales.
10
Lección 10 · Avanzado
Configurar un proyecto
TypeScript se instala con npm en cada proyecto y se configura con tsconfig.json. Activa "strict": true: incluye las comprobaciones más útiles, como las de null.
npx tsc compila todo el proyecto según esa configuración, y npx tsc --noEmit solo comprueba los tipos sin generar archivos.
Ejemplo práctico
# En la terminal
npm install --save-dev typescript
npx tsc --init # crea tsconfig.json
# tsconfig.json (fragmento)
{
"compilerOptions": {
"strict": true,
"target": "es2022",
"outDir": "dist"
}
}
npx tsc # compila a la carpeta dist
Ejercicio
Completa la opción que activa todas las comprobaciones estrictas.
"compilerOptions": { "": true }
Ver la solución
Respuesta:strict. strict agrupa opciones como strictNullChecks o noImplicitAny.
11
Lección 11 · Avanzado
Tipos literales y as const
Un tipo literal admite un valor concreto y no cualquiera de su clase: "S" | "M" | "L" solo acepta esos tres textos. Es la forma habitual de tipar opciones fijas, como tallas, estados de un pedido o direcciones, y el editor te las sugiere al escribir.
as const convierte un objeto o una lista en valores literales de solo lectura. Con (typeof lista)[number] obtienes la unión de sus valores sin escribirlos dos veces.
Ejemplo práctico
type Talla = "S" | "M" | "L";
function precioCamiseta(talla: Talla): number {
return talla === "L" ? 15 : 12;
}
console.log(precioCamiseta("M")); // 12
// precioCamiseta("XL"); // error: "XL" no es una Talla
const ESTADOS = ["pendiente", "enviado", "entregado"] as const;
type Estado = (typeof ESTADOS)[number]; // "pendiente" | "enviado" | "entregado"
const estado: Estado = "enviado";
console.log(ESTADOS.indexOf(estado)); // 1
Escribe el código
Declara el tipo Direccion que solo admita "norte", "sur", "este" y "oeste", y escribe la función mover(dir: Direccion): string que devuelva "Vas hacia el " + dir.
Tab escribe una sangría; para salir del editor con el teclado, pulsa Esc y luego Tab. Ctrl + Intro corrige.
Una solución posible
type Direccion = "norte" | "sur" | "este" | "oeste";
function mover(dir: Direccion): string {
return "Vas hacia el " + dir;
}
Con este tipo, mover("arriba") da un error antes de ejecutar el programa, y el editor sugiere las cuatro opciones válidas.
Ver la solución
type Direccion = "norte" | "sur" | "este" | "oeste";
function mover(dir: Direccion): string {
return "Vas hacia el " + dir;
}
Con este tipo, mover("arriba") da un error antes de ejecutar el programa, y el editor sugiere las cuatro opciones válidas.
12
Lección 12 · Avanzado
Uniones discriminadas
Cuando una variable puede ser varias formas de objeto, dales a todas una propiedad común con un valor literal distinto, como tipo: "circulo" o tipo: "rectangulo". Al comprobarla en un switch, TypeScript sabe qué otras propiedades existen en cada caso.
Si añades el caso default asignando el valor a una variable de tipo never, TypeScript te avisará cuando agregues una forma nueva y olvides tratarla en el switch.
Ejemplo práctico
type Forma =
| { tipo: "circulo"; radio: number }
| { tipo: "rectangulo"; ancho: number; alto: number };
function area(forma: Forma): number {
switch (forma.tipo) {
case "circulo":
return Math.PI * forma.radio ** 2; // aquí solo existe radio
case "rectangulo":
return forma.ancho * forma.alto; // aquí, ancho y alto
default: {
const imposible: never = forma; // error si falta algún caso
return imposible;
}
}
}
console.log(area({ tipo: "rectangulo", ancho: 3, alto: 4 })); // 12
console.log(area({ tipo: "circulo", radio: 1 }).toFixed(2)); // "3.14"
Ejercicio
En el case "circulo", ¿por qué TypeScript permite usar forma.radio sin error?
Ver la solución
Respuesta: Porque al comprobar forma.tipo sabe que en ese caso es un círculo. La propiedad tipo «discrimina» la unión: cada valor corresponde a una sola forma, y TypeScript estrecha el tipo en cada case.
13
Lección 13 · Avanzado
unknown y guardas de tipo propias
Los datos que llegan de fuera (un JSON, lo que escribe una persona) no tienen un tipo garantizado. Tiparlos como unknown en vez de any obliga a comprobarlos antes de usarlos: TypeScript no te deja acceder a nada hasta que demuestres qué son.
Una guarda de tipo es una función que devuelve valor is Tipo: si devuelve true, TypeScript trata el valor como ese tipo a partir de ahí. Así la comprobación se escribe una vez y se reutiliza.
Ejemplo práctico
interface Usuario {
nombre: string;
edad: number;
}
function esUsuario(dato: unknown): dato is Usuario {
return typeof dato === "object" && dato !== null
&& typeof (dato as Usuario).nombre === "string"
&& typeof (dato as Usuario).edad === "number";
}
const recibido: unknown = JSON.parse('{"nombre": "Ana", "edad": 31}');
if (esUsuario(recibido)) {
console.log(recibido.nombre.toUpperCase()); // "ANA": aquí ya es Usuario
} else {
console.log("Datos con un formato inesperado");
}
Escribe el código
Escribe la guarda de tipo esTexto(valor: unknown): devuelve true solo si valor es un texto, y su tipo de retorno tiene que ser valor is string.
Tab escribe una sangría; para salir del editor con el teclado, pulsa Esc y luego Tab. Ctrl + Intro corrige.
Una solución posible
function esTexto(valor: unknown): valor is string {
return typeof valor === "string";
}
Con : valor is string, después de if (esTexto(x)) TypeScript te deja usar los métodos de texto sobre x.
Ver la solución
function esTexto(valor: unknown): valor is string {
return typeof valor === "string";
}
Con : valor is string, después de if (esTexto(x)) TypeScript te deja usar los métodos de texto sobre x.
14
Lección 14 · Avanzado
Promesas con tipo: async y Promise<T>
Una función async siempre devuelve una promesa. Su tipo se escribe Promise<T>, donde T es lo que obtendrás con await: Promise<Usuario>, Promise<string[]>… Así, quien la llame sabe qué recibirá.
Los datos que llegan por red se tratan con cuidado: response.json() devuelve any, así que conviene darles un tipo (o comprobarlos con una guarda) en el momento de recibirlos.
Ejemplo práctico
interface Usuario {
id: number;
nombre: string;
}
// Simula una petición que tarda medio segundo
function pedirUsuario(id: number): Promise<Usuario> {
return new Promise((resolve) => {
setTimeout(() => resolve({ id, nombre: "Ana" }), 500);
});
}
async function saludar(id: number): Promise<string> {
const usuario = await pedirUsuario(id); // usuario: Usuario
return `Hola, ${usuario.nombre}`;
}
saludar(1).then((texto) => console.log(texto)); // "Hola, Ana"
Ejercicio
Completa el tipo de retorno de una función async que obtiene un Usuario.
async function cargar(): <Usuario> {
Ver la solución
Respuesta:Promise. Las funciones async devuelven Promise<T>; con await se obtiene el T.
15
Lección 15 · Avanzado
Tuplas y datos de solo lectura
Una tupla es una lista de tamaño fijo en la que cada posición tiene su propio tipo: [string, number] es exactamente un texto y después un número. Se usa para devolver dos valores juntos, como hace useState en React.
readonly impide modificar una propiedad o una lista después de crearla: TypeScript da un error si intentas cambiarla. Sirve para dejar claro qué datos no deben cambiar, como una configuración.
Ejemplo práctico
function minMax(numeros: number[]): [number, number] {
return [Math.min(...numeros), Math.max(...numeros)];
}
const [menor, mayor] = minMax([7, 2, 9]);
console.log(menor, mayor); // 2 9
interface Config {
readonly url: string;
readonly reintentos: number;
}
const config: Config = { url: "https://api.ejemplo.es", reintentos: 3 };
// config.reintentos = 5; // error: es de solo lectura
const dias: readonly string[] = ["lunes", "martes"];
// dias.push("miércoles"); // error: no existe push en una lista readonly
console.log(config.url, dias.length);
Ejercicio
¿Qué tipo describe exactamente un par «nombre y edad», como ["Ana", 31]?
Ver la solución
Respuesta:[string, number]. [string, number] es una tupla: dos posiciones con su tipo. (string | number)[] admitiría cualquier cantidad de elementos y en cualquier orden.
¿Has terminado? Sigue practicando
Repasa los conceptos en el diccionario, practica con estas herramientas o prueba otro lenguaje: las ideas que has aprendido (variables, condiciones, funciones, objetos…) te servirán en cualquiera.
* Porcentaje de personas que dijeron haber usado TypeScript en la Stack Overflow Developer Survey 2026: Technology (pregunta sobre lenguajes de programación, de scripts y de marcado; todas las respuestas). El progreso de las lecciones se guarda solo en este navegador.
¿Nos dejas medir las visitas?
Con tu permiso usamos Google Analytics, que guarda cookies, para saber cuántas personas visitan la web y qué páginas les resultan útiles. No usamos cookies de publicidad y tus archivos nunca salen de tu navegador. Puedes cambiar de opinión cuando quieras en «Configurar cookies», al pie de la página. Más información.