Saltar al contenido principal
Logo FPCode

TypeScript: fundamentos

Contenido

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

Í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

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.

Esquema propuesto del flujo de TypeScript a JavaScript

2. Instalar y ejecutar TypeScript

Para trabajar con TypeScript necesitas Node.js y npm.

Comprueba la instalación:

Terminal window
node --version
npm --version

Crea una carpeta de proyecto e inicialízala:

Terminal window
mkdir proyecto-typescript
cd proyecto-typescript
npm init -y

Instala TypeScript como dependencia de desarrollo:

Terminal window
npm install typescript --save-dev

Puedes ejecutar el compilador con npx:

Terminal window
npx tsc --version

No 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.json

Archivo 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:

Terminal window
npx tsc

TypeScript generará JavaScript en dist/index.js. Puedes ejecutarlo con Node:

Terminal window
node dist/index.js

4. 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:

TipoUsoEjemplo
stringTexto'Hola'
numberNúmeros enteros o decimales42, 3.14
booleanValores lógicostrue, false
nullAusencia intencionada de valornull
undefinedValor no definidoundefined
objectEstructuras 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 string
let puntos = 10; // TypeScript infiere number

No 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.

estado-pedido.ts
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-pedido.ts
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:

ValorSignificado habitual
undefinedNo se ha asignado valor o una propiedad opcional no existe.
nullSe indica explícitamente ausencia de valor.

Ejemplo:

usuario.ts
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.

any.ts
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.

unknown.ts
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.

never.ts
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:

calculadora.ts
export function sumar(a: number, b: number): number {
return a + b;
}

Archivo que importa y usa esa función:

index.ts
import { sumar } from './calculadora';
console.log(sumar(2, 3));

También puedes exportar tipos:

producto.ts
export type Producto = {
id: number;
nombre: string;
precio: number;
};

Cuando importas solo un tipo, puedes usar import type:

carrito.ts
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ónSignificado
targetVersión de JavaScript generada.
moduleSistema de módulos de salida.
strictActiva comprobaciones estrictas.
noEmitOnErrorEvita generar JavaScript si hay errores de tipos.
outDirCarpeta 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

ErrorEjemploCorrección
Usar any para todolet dato: anyusar tipos concretos o unknown
No tipar parámetrosfunction sumar(a, b)function sumar(a: number, b: number): number
Confundir opcional con nulonombre?: string y esperar siempre nullcomprobar undefined
Ignorar errores del compiladorcompilar aunque haya avisosrevisar el mensaje y corregir la causa
Tipar demasiado prontoescribir tipos enormes al empezarextraer interfaces pequeñas y claras

15. Buenas prácticas iniciales

  • Activa strict desde el principio.
  • Aprovecha la inferencia cuando el tipo sea evidente.
  • Escribe tipos explícitos en parámetros y retornos de funciones públicas.
  • Evita any salvo 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 unknown antes que any cuando 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.