Antes de que existieran los módulos ES, TypeScript introdujo los namespaces como mecanismo para organizar código en espacios de nombres y evitar colisiones de nombres globales. En 2026 los namespaces son una caracterÃstica obsoleta para código de aplicación, pero siguen siendo la herramienta correcta en un contexto muy especÃfico: los ficheros de declaración ambient para librerÃas que exponen globales.
La historia: organizar código antes de los módulos
En los primeros años de TypeScript (2012-2014), los proyectos web concatenaban ficheros JavaScript sin sistema de módulos. Los namespaces (entonces llamados "módulos internos") permitÃan agrupar código bajo un nombre para evitar contaminar el scope global:
// Sintaxis antigua con namespace (no uses esto en código nuevo)
namespace MiApp {
export interface Usuario {
id: number;
nombre: string;
}
export function saludar(u: Usuario): string {
return `Hola, ${u.nombre}`;
}
export namespace Utilidades {
export function formatear(fecha: Date): string {
return fecha.toISOString();
}
}
}
// Uso:
const u: MiApp.Usuario = { id: 1, nombre: "Ana" };
MiApp.saludar(u);
MiApp.Utilidades.formatear(new Date());
Por qué hoy debes usar módulos ES
Los módulos ES (ESM) resuelven el mismo problema de forma más limpia y estándar. Cada fichero es su propio módulo con su propio scope. Las importaciones son explÃcitas. El código es analizable por bundlers para tree-shaking. El ecosistema completo de JavaScript (Node.js, navegadores, Deno, Bun) soporta ESM de forma nativa.
Los namespaces en código de aplicación generan código JavaScript con objetos anidados que los bundlers modernos no pueden optimizar bien. No hay ventaja técnica sobre los módulos ES y sà hay desventajas claras.
El único caso de uso válido: ficheros .d.ts de ambient declarations
Los namespaces siguen siendo la herramienta correcta para describir librerÃas antiguas que exponen objetos en el scope global, como jQuery, Google Analytics o librerÃas UMD que añaden propiedades a window:
// types/jquery/index.d.ts
declare namespace jQuery {
function ajax(url: string, opciones?: AjaxOptions): Promise;
function get(url: string): Promise;
interface AjaxOptions {
method?: "GET" | "POST" | "PUT" | "DELETE";
data?: unknown;
headers?: Record;
}
}
declare const $: typeof jQuery;
// types/google-analytics/index.d.ts
declare namespace gtag {
function (comando: "event", nombre: string, params?: Record): void;
function (comando: "config", id: string, params?: Record): void;
}
declare function gtag(...args: unknown[]): void;
Cómo migrar de namespaces a módulos
Si tienes un proyecto heredado con namespaces, la migración a módulos ES es directa:
- Añade
exporta las declaraciones que quieres exponer fuera del fichero. - Elimina el bloque
namespace NombreApp { ... }que lo envuelve. - Sustituye los usos de
NombreApp.Cosapor imports explÃcitos:import { Cosa } from "./fichero". - Configura
"module": "ESNext"o"NodeNext"en tsconfig si no lo está ya.
// Antes (namespace)
namespace MiApp {
export interface Config { debug: boolean }
export function iniciar(cfg: Config): void { /* ... */ }
}
// Después (módulo ES)
export interface Config { debug: boolean }
export function iniciar(cfg: Config): void { /* ... */ }
La migración de namespaces a módulos normalmente no cambia la lógica de la aplicación, solo la forma en que el código se organiza y se importa. El resultado es código más moderno, compatible con herramientas actuales y mejor analizable por bundlers.
Imagen: Pexels / Myburgh Roux
