Los módulos en TypeScript son archivos independientes que exportan e importan código para mantener tu proyecto organizado y reutilizable.
Cuando un proyecto crece, tener todo el código en un solo archivo se vuelve imposible de mantener. Los módulos resuelven ese problema dividiendo la lógica en piezas pequeñas y especializadas.
¿Qué es un módulo en TypeScript?
En TypeScript, cualquier archivo que use export o import es automáticamente un módulo. Sin esas palabras clave, el archivo se trata como un script global, lo que puede causar conflictos de nombres.
Un módulo tiene dos responsabilidades claras:
- Exportar lo que otros archivos pueden usar.
- Importar lo que necesita de otros archivos.
Sintaxis de exportación e importación
Exportación nombrada
Usa export directamente antes de una función, clase, interfaz o variable.
// archivo: calculos.ts
export function calcularIVA(precio: number): number {
return precio * 0.16;
}
export function calcularTotal(precio: number): number {
return precio + calcularIVA(precio);
}
Puedes exportar varias cosas del mismo archivo. Cada una mantiene su nombre original.
Importación nombrada
// archivo: main.ts
import { calcularIVA, calcularTotal } from "./calculos";
const precioProducto = 850;
console.log("IVA:", calcularIVA(precioProducto)); // IVA: 136
console.log("Total:", calcularTotal(precioProducto)); // Total: 986
Usa llaves {} para importar exportaciones nombradas. La ruta "./calculos" es relativa al archivo actual.
Exportación por defecto
Cada módulo puede tener una sola exportación por defecto con export default.
// archivo: cliente.ts
export default class Cliente {
constructor(public nombre: string, public correo: string) {}
presentarse(): string {
return `Hola, soy ${this.nombre} y mi correo es ${this.correo}.`;
}
}
// archivo: main.ts
import Cliente from "./cliente";
const c = new Cliente("Ana Torres", "ana@liverpool.com.mx");
console.log(c.presentarse());
// Hola, soy Ana Torres y mi correo es ana@liverpool.com.mx.
Con export default no usas llaves al importar y puedes darle el nombre que quieras.
Ejemplo completo: sistema de inventario para FEMSA
Imagina que construyes un pequeño sistema de inventario. Divides el proyecto en tres archivos.
Archivo tipos.ts — define las interfaces compartidas:
export interface Producto {
id: number;
nombre: string;
precio: number;
stock: number;
}
export interface ResultadoVenta {
producto: string;
cantidad: number;
total: number;
}
Archivo inventario.ts — lógica de negocio:
import { Producto, ResultadoVenta } from "./tipos";
const productos: Producto[] = [
{ id: 1, nombre: "Coca-Cola 600ml", precio: 18, stock: 200 },
{ id: 2, nombre: "Agua CIEL 1L", precio: 12, stock: 350 },
];
export function buscarProducto(id: number): Producto | undefined {
return productos.find((p) => p.id === id);
}
export function registrarVenta(id: number, cantidad: number): ResultadoVenta | string {
const producto = buscarProducto(id);
if (!producto) return "Producto no encontrado.";
if (producto.stock < cantidad) return "Stock insuficiente.";
producto.stock -= cantidad;
const total = producto.precio * cantidad;
return { producto: producto.nombre, cantidad, total };
}
Archivo main.ts — punto de entrada:
import { registrarVenta } from "./inventario";
const resultado = registrarVenta(1, 10);
if (typeof resultado === "string") {
console.log("Error:", resultado);
} else {
console.log(`Venta: ${resultado.producto}`);
console.log(`Cantidad: ${resultado.cantidad}`);
console.log(`Total: $${resultado.total.toLocaleString("es-MX")}`);
// Total: $180
}
Esta estructura separa tipos, lógica y ejecución en archivos distintos. Es fácil de leer y de modificar.
Cómo organizar carpetas en un proyecto TypeScript
Una estructura de carpetas estándar para proyectos medianos en México luce así:
proyecto-femsa/
├── src/
│ ├── tipos/
│ │ └── producto.ts
│ ├── servicios/
│ │ └── inventario.ts
│ ├── utilidades/
│ │ └── calculos.ts
│ └── main.ts
├── tsconfig.json
└── package.json
src/tipos/ guarda interfaces y tipos compartidos. src/servicios/ contiene la lógica de negocio. src/utilidades/ aloja funciones de apoyo como formateos o cálculos.
Re-exportar desde un archivo índice
Cuando tienes muchos módulos en una carpeta, crear un archivo index.ts simplifica las importaciones.
// archivo: src/tipos/index.ts
export { Producto } from "./producto";
export { ResultadoVenta } from "./venta";
export { Cliente } from "./cliente";
Ahora en cualquier otro archivo puedes importar todo desde una sola ruta:
import { Producto, Cliente } from "./tipos";
Esto evita rutas largas como "../../tipos/producto" y hace el código más limpio.
Tabla de referencia: exportación e importación
| Tipo | Sintaxis de exportación | Sintaxis de importación |
|---|---|---|
| Nombrada (función) | export function foo() {} |
import { foo } from "./archivo" |
| Nombrada (interfaz) | export interface Bar {} |
import { Bar } from "./archivo" |
| Por defecto (clase) | export default class Baz {} |
import Baz from "./archivo" |
| Re-exportación | export { foo } from "./otro" |
import { foo } from "./indice" |
| Todo el módulo | — | import * as util from "./util" |
Importar todo el módulo con * as
Cuando necesitas muchas exportaciones de un archivo, puedes importarlas todas bajo un alias:
import * as Calculos from "./calculos";
const precio = 1200;
console.log(Calculos.calcularIVA(precio)); // 192
console.log(Calculos.calcularTotal(precio)); // 1392
Esta forma es útil cuando el módulo funciona como una librería de utilidades.
Errores comunes
1. Olvidar la extensión o la ruta relativa
// ❌ Incorrecto
import { calcularIVA } from "calculos";
// ✅ Correcto
import { calcularIVA } from "./calculos";
TypeScript espera rutas relativas con ./ o ../. Sin ellas, buscará en node_modules y fallará.
2. Mezclar exportación por defecto con exportaciones nombradas sin orden
// ❌ Confuso: el default y los nombrados mezclados sin criterio
export default function principal() {}
export default function secundaria() {} // Error: solo puede haber un default
Cada archivo solo admite un export default. Si tienes más de una función principal, usa exportaciones nombradas.
3. Importar con nombre incorrecto en exportación nombrada
// calculos.ts exporta:
export function calcularIVA() {}
// ❌ Nombre diferente sin alias
import { calculaIVA } from "./calculos"; // Error: no existe "calculaIVA"
// ✅ Correcto o con alias
import { calcularIVA as IVA } from "./calculos";
Las exportaciones nombradas son exactamente iguales al nombre original. Puedes usar as para renombrarlas al importar.
4. No configurar moduleResolution en tsconfig.json
Para que TypeScript resuelva correctamente las rutas de módulos, verifica tu tsconfig.json:
{
"compilerOptions": {
"module": "commonjs",
"moduleResolution": "node",
"outDir": "./dist",
"rootDir": "./src",
"strict": true
}
}
Sin moduleResolution: "node", TypeScript puede no encontrar tus archivos locales ni los paquetes de node_modules.
Puntos clave
- Los módulos dividen el código en archivos especializados que se comunican mediante
exporteimport. - Usa exportaciones nombradas para múltiples elementos por archivo y exportación por defecto para el elemento principal del archivo.
- Un archivo
index.tsen cada carpeta centraliza las re-exportaciones y simplifica las rutas de importación en el resto del proyecto. - Organizar el proyecto en carpetas como
tipos/,servicios/yutilidades/mejora la legibilidad y el mantenimiento a largo plazo. - Verifica siempre las rutas relativas y la configuración de
moduleResolutionentsconfig.jsonpara evitar errores de compilación.