TypeScript: fundamentos
Contenido
- Introducción
- Conocimiento previo
- Referencias
- Índice
- 1. ¿Qué es TypeScript?
- 2. Instalar y ejecutar TypeScript
- 3. Crear un primer programa
- 4. Tipos básicos
- 5. Inferencia y anotaciones de tipo
- 6. Funciones tipadas
- 7. Arrays, tuplas y objetos
- 8. Interfaces y alias de tipo
- 9. Uniones y estrechamiento de tipos
- 10. Tipos literales, null y undefined
- 11. any, unknown y never
- 12. Módulos e importaciones
- 13. Configuración básica con tsconfig
- 14. Errores frecuentes
- 15. Buenas prácticas iniciales
- 16. Ejercicios
Tutorial introductorio de TypeScript para desarrollo web en entorno cliente: tipos estáticos, inferencia, funciones tipadas, arrays, objetos, interfaces, uniones, literales, estrechamiento de tipos, módulos y configuración básica del compilador.
Introducción
TypeScript es un lenguaje que se construye sobre JavaScript y añade un sistema de tipos estáticos. Esto significa que puedes seguir escribiendo JavaScript, pero con información adicional sobre el tipo de datos que espera cada variable, parámetro, objeto o función.
El objetivo principal de TypeScript no es cambiar cómo se ejecuta JavaScript en el navegador, sino ayudarte a detectar errores antes de ejecutar el programa. TypeScript revisa el código durante el desarrollo y después genera JavaScript normal.
En este tutorial aprenderás los fundamentos necesarios para empezar a usar TypeScript en proyectos web de forma segura y progresiva.
Conocimiento previo
Referencias
- Documentación oficial de TypeScript
- TypeScript Handbook
- TypeScript for JavaScript Programmers
- TSConfig Reference
Índice
- ¿Qué es TypeScript?
- Instalar y ejecutar TypeScript
- Crear un primer programa
- Tipos básicos
- Inferencia y anotaciones de tipo
- Funciones tipadas
- Arrays, tuplas y objetos
- Interfaces y alias de tipo
- Uniones y estrechamiento de tipos
- Tipos literales, null y undefined
- any, unknown y never
- Módulos e importaciones
- Configuración básica con tsconfig
- Errores frecuentes
- Buenas prácticas iniciales
- Ejercicios
1. ¿Qué es TypeScript?
JavaScript permite escribir código muy flexible, pero esa flexibilidad también puede provocar errores difíciles de encontrar.
Ejemplo en JavaScript:
function calcularTotal(precio, cantidad) { return precio * cantidad;}
console.log(calcularTotal('10', 3));El código puede ejecutarse, pero hay una incoherencia: precio debería ser un número, no un texto.
Con TypeScript podemos expresar esa intención:
function calcularTotal(precio: number, cantidad: number): number { return precio * cantidad;}
console.log(calcularTotal(10, 3));Si alguien intenta pasar un string, TypeScript avisará durante el desarrollo.
TypeScript aporta principalmente:
- Detección temprana de errores.
- Mejor autocompletado en el editor.
- Código más fácil de mantener.
- Contratos claros entre funciones, objetos y módulos.
- Refactorizaciones más seguras en proyectos grandes.
TODO: añadir imagen con el flujo TypeScript a JavaScript y ejecución en navegador o Node.js.
2. Instalar y ejecutar TypeScript
Para trabajar con TypeScript necesitas Node.js y npm.
Comprueba la instalación:
node --versionnpm --versionCrea una carpeta de proyecto e inicialízala:
mkdir proyecto-typescriptcd proyecto-typescriptnpm init -yInstala TypeScript como dependencia de desarrollo:
npm install typescript --save-devPuedes ejecutar el compilador con npx:
npx tsc --versionNo es necesario instalar TypeScript globalmente para empezar. Instalarlo en cada proyecto facilita que todos los miembros usen la misma versión.
3. Crear un primer programa
Crea esta estructura:
proyecto-typescript/├── src/│ └── index.ts├── package.json└── tsconfig.jsonArchivo src/index.ts:
const mensaje: string = 'Hola, TypeScript';
console.log(mensaje);Archivo tsconfig.json:
{ "compilerOptions": { "target": "ES2022", "module": "ESNext", "strict": true, "outDir": "dist" }, "include": ["src"]}Compila el proyecto:
npx tscTypeScript generará JavaScript en dist/index.js. Puedes ejecutarlo con Node:
node dist/index.js4. Tipos básicos
TypeScript incluye los tipos primitivos de JavaScript y permite anotarlos explícitamente.
const nombre: string = 'Ana';const edad: number = 22;const activo: boolean = true;const sinValor: null = null;let pendiente: undefined;Tipos frecuentes:
| Tipo | Uso | Ejemplo |
|---|---|---|
string | Texto | 'Hola' |
number | Números enteros o decimales | 42, 3.14 |
boolean | Valores lógicos | true, false |
null | Ausencia intencionada de valor | null |
undefined | Valor no definido | undefined |
object | Estructuras con propiedades | { nombre: 'Ana' } |
También existe any, pero conviene evitarlo al empezar porque desactiva las comprobaciones de tipo.
let dato: any = 'texto';dato = 10;dato = true;Si necesitas representar un valor desconocido, normalmente es preferible unknown, porque obliga a comprobar el tipo antes de usarlo.
5. Inferencia y anotaciones de tipo
TypeScript puede inferir tipos a partir del valor inicial.
const ciudad = 'Valencia'; // TypeScript infiere stringlet puntos = 10; // TypeScript infiere numberNo siempre hace falta escribir el tipo:
const precio = 19.99;const cantidad = 3;const total = precio * cantidad;Las anotaciones son útiles cuando:
- La variable se declarará sin valor inicial.
- Queremos documentar la intención.
- Definimos parámetros y retornos de funciones.
- Trabajamos con objetos o estructuras compartidas.
let descuento: number;
descuento = 10;6. Funciones tipadas
Las funciones pueden definir tipos para sus parámetros y para el valor que devuelven.
function sumar(a: number, b: number): number { return a + b;}
const resultado = sumar(4, 6);Si una función no devuelve nada, se usa void:
function mostrarMensaje(mensaje: string): void { console.log(mensaje);}También se pueden tipar funciones flecha:
const calcularIva = (precio: number, iva: number): number => { return precio * iva;};Los parámetros opcionales se indican con ?:
function saludar(nombre: string, grupo?: string): string { if (grupo) { return `Hola, ${nombre}. Grupo: ${grupo}`; }
return `Hola, ${nombre}`;}7. Arrays, tuplas y objetos
Un array tipado indica qué tipo de elementos puede contener.
const notas: number[] = [7, 8, 5];const lenguajes: string[] = ['JavaScript', 'TypeScript'];Otra sintaxis equivalente es Array<T>:
const precios: Array<number> = [10, 20, 30];Una tupla define una posición concreta para cada tipo.
const coordenada: [number, number] = [10, 20];const respuesta: [number, string] = [200, 'OK'];Los objetos se pueden tipar indicando sus propiedades.
const alumno: { nombre: string; edad: number; activo: boolean } = { nombre: 'Lucía', edad: 19, activo: true,};Para objetos que se repiten, conviene extraer la forma a una interfaz o alias de tipo.
8. Interfaces y alias de tipo
Una interfaz define la forma que debe tener un objeto.
interface Usuario { id: number; nombre: string; email: string; activo?: boolean;}
const usuario: Usuario = { id: 1, nombre: 'Ana', email: 'ana@example.com',};La propiedad activo? es opcional.
Un alias de tipo también permite dar nombre a una estructura.
type Producto = { id: number; nombre: string; precio: number;};Como regla práctica, puedes usar interface para describir objetos y type cuando necesites uniones, tipos primitivos con nombre o combinaciones más flexibles.
9. Uniones y estrechamiento de tipos
Una unión permite aceptar más de un tipo.
function formatearId(id: number | string): string { return `ID-${id}`;}Cuando una variable puede tener varios tipos, TypeScript obliga a comprobar el caso concreto antes de usar operaciones específicas.
function mostrarValor(valor: string | number): void { if (typeof valor === 'string') { console.log(valor.toUpperCase()); return; }
console.log(valor.toFixed(2));}Este proceso se llama estrechamiento de tipos o narrowing. Dentro del if, TypeScript sabe que valor es un string; fuera de ese bloque, mantiene la información completa de la unión.
10. Tipos literales, null y undefined
Un tipo literal representa un valor concreto, no cualquier valor de un tipo amplio.
type EstadoPedido = 'pendiente' | 'pagado' | 'enviado' | 'cancelado';
let estado: EstadoPedido = 'pendiente';estado = 'pagado';Si intentas asignar un texto que no está en la unión, TypeScript avisará.
estado = 'preparando';Los literales son útiles para estados, roles, categorías, modos de visualización o respuestas controladas.
null y undefined representan ausencia de valor, pero no significan exactamente lo mismo:
| Valor | Significado habitual |
|---|---|
undefined | No se ha asignado valor o una propiedad opcional no existe. |
null | Se indica explícitamente ausencia de valor. |
Ejemplo:
type Usuario = { id: number; nombre: string; email?: string; ultimoAcceso: Date | null;};Con strict activado, TypeScript obliga a comprobar estos casos antes de usarlos como si siempre existieran.
11. any, unknown y never
any desactiva las comprobaciones de TypeScript para un valor. Puede ser útil en migraciones muy concretas, pero no debe usarse como solución general.
let dato: any = 'texto';dato.toUpperCase();dato.noExiste();TypeScript no avisará aunque noExiste() sea un error en ejecución.
unknown representa un valor desconocido, pero obliga a comprobarlo antes de usarlo.
function mostrar(valor: unknown): void { if (typeof valor === 'string') { console.log(valor.toUpperCase()); return; }
console.log('Valor no textual');}never representa un caso que no debería ocurrir. Es útil para comprobar que hemos cubierto todos los casos de una unión.
type Estado = 'cargando' | 'ok' | 'error';
function textoEstado(estado: Estado): string { switch (estado) { case 'cargando': return 'Cargando'; case 'ok': return 'Correcto'; case 'error': return 'Error'; default: { const casoImposible: never = estado; return casoImposible; } }}12. Módulos e importaciones
En proyectos reales, el código TypeScript se reparte entre varios archivos. Para compartir funciones, tipos o constantes se usan módulos.
Archivo que exporta una función:
export function sumar(a: number, b: number): number { return a + b;}Archivo que importa y usa esa función:
import { sumar } from './calculadora';
console.log(sumar(2, 3));También puedes exportar tipos:
export type Producto = { id: number; nombre: string; precio: number;};Cuando importas solo un tipo, puedes usar import type:
import type { Producto } from './producto';
export function calcularTotal(productos: Producto[]): number { return productos.reduce((total, producto) => total + producto.precio, 0);}Esta separación ayuda a organizar aplicaciones con muchos componentes, servicios o modelos de datos.
13. Configuración básica con tsconfig
El archivo tsconfig.json indica al compilador cómo debe revisar y generar el proyecto.
Una configuración inicial razonable sería:
{ "compilerOptions": { "target": "ES2022", "module": "ESNext", "strict": true, "moduleResolution": "Bundler", "noEmitOnError": true, "outDir": "dist" }, "include": ["src"]}Opciones importantes:
| Opción | Significado |
|---|---|
target | Versión de JavaScript generada. |
module | Sistema de módulos de salida. |
strict | Activa comprobaciones estrictas. |
noEmitOnError | Evita generar JavaScript si hay errores de tipos. |
outDir | Carpeta donde se escribe el JavaScript generado. |
Para aprender, conviene mantener strict: true. Aunque al principio parezca más exigente, ayuda a detectar errores reales.
14. Errores frecuentes
| Error | Ejemplo | Corrección |
|---|---|---|
Usar any para todo | let dato: any | usar tipos concretos o unknown |
| No tipar parámetros | function sumar(a, b) | function sumar(a: number, b: number): number |
| Confundir opcional con nulo | nombre?: string y esperar siempre null | comprobar undefined |
| Ignorar errores del compilador | compilar aunque haya avisos | revisar el mensaje y corregir la causa |
| Tipar demasiado pronto | escribir tipos enormes al empezar | extraer interfaces pequeñas y claras |
15. Buenas prácticas iniciales
- Activa
strictdesde el principio. - Aprovecha la inferencia cuando el tipo sea evidente.
- Escribe tipos explícitos en parámetros y retornos de funciones públicas.
- Evita
anysalvo que tengas una razón concreta. - Usa nombres claros para interfaces y tipos.
- Modela primero los datos importantes de la aplicación.
- Corrige los errores de tipos en lugar de silenciarlos.
- Usa
unknownantes queanycuando recibas datos externos. - Divide los tipos compartidos en módulos cuando empiecen a repetirse.
16. Ejercicios
Ejercicio 1: Variables y funciones tipadas
Declara variables tipadas para nombre, edad, email y activo.
Ejercicio 2: Función para calcular área
Crea una función calcularAreaRectangulo(base, altura) con parámetros y retorno de tipo number.
Ejercicio 3: Interfaz para alumnos
Define una interfaz Alumno con id, nombre, curso y notaMedia.
Ejercicio 4: Filtrar alumnos aprobados
Crea un array de Alumno y filtra los alumnos con nota media igual o superior a 5.
Ejercicio 5: Formatear datos
Escribe una función que reciba string | number y devuelva siempre un string formateado.
Ejercicio 6: Modelar estados con literales
Crea un tipo EstadoPedido con cuatro valores posibles y una función que devuelva un mensaje para cada estado.
Ejercicio 7: Usar unknown
Crea una función que reciba unknown, compruebe si es un número y devuelva su doble solo en ese caso.
Ejercicio 8: Separar módulos
Crea un archivo producto.ts con un tipo Producto y otro archivo carrito.ts que importe ese tipo para calcular el total de una lista.
Con estos fundamentos ya puedes leer y escribir código TypeScript básico. El siguiente paso es aprender a modelar casos más complejos con genéricos, tipos utilitarios, uniones discriminadas y tipos derivados.