Menu

Namespace en TypeScript: qué es y cuándo usarlo

Un namespace de TypeScript agrupa valores y tipos bajo un mismo nombre y compila a un objeto normal. Aprende la sintaxis, cómo se fusionan los namespaces entre sí y con funciones y clases, por qué los ES modules los sustituyeron y dónde siguen apareciendo: archivos de declaraciones y ampliación del ámbito global.

Esta página incluye editores ejecutables: edita, ejecuta y ve el resultado al instante.

Un namespace es un bloque con nombre que agrupa funciones, constantes y tipos bajo un mismo nombre. Los miembros marcados con export son accesibles como Name.member; el resto queda privado dentro del bloque. En tiempo de ejecución un namespace es un objeto normal.

A qué compila un namespace

Los namespaces son una de las pocas características de TypeScript que generan código. El bloque de arriba se convierte en una función que rellena un objeto:

var Geometry;
(function (Geometry) {
    const TAU = Math.PI * 2;
    function circumference(radius) {
        return TAU * radius;
    }
    Geometry.circumference = circumference;
    function area(radius) {
        return Math.PI * radius ** 2;
    }
    Geometry.area = area;
})(Geometry || (Geometry = {}));

Es el patrón que usaba el código JavaScript antes de que existieran los módulos para no poner todos los nombres en el ámbito global. TAU es una variable local de la función, y por eso el resto del código no puede verla. Intentar leerla es un error en tiempo de compilación:

El compilador informa index.ts(10,17): error TS2339: Property 'rate' does not exist on type 'typeof Tax'. Añade export delante de const rate y las dos líneas se ejecutan.

Anidamiento, fusión y alias

Los namespaces se pueden anidar, y dos bloques con el mismo nombre se fusionan en uno. El Geometry || (Geometry = {}) de la salida es lo que lo hace posible: el segundo bloque añade cosas al objeto existente. import X = A.B crea un alias corto.

namespace A.B.C { } es una forma abreviada de tres bloques anidados. La forma antigua module Shop { } significa lo mismo pero ahora se rechaza con el error TS1540, A 'namespace' declaration should not be declared using the 'module' keyword. Please use the 'namespace' keyword instead.

Fusión con funciones y clases

Un namespace puede compartir nombre con una función, una clase o un enum y añadirle miembros. Sigue siendo la forma más limpia de describir una función que además lleva propiedades, o una clase con funciones auxiliares añadidas.

En una clase, un método static hace el mismo trabajo y es JavaScript normal. En una función, también puedes prescindir del namespace y asignar format.prefix = "$" justo después de la declaración; TypeScript registra las propiedades asignadas así.

Namespaces frente a módulos

Antes de los ES modules, un programa grande de TypeScript eran muchos archivos de script que compartían namespaces globales, unidos con /// <reference path="..." /> y compilados en un solo archivo con outFile. Los módulos sustituyeron eso: cada archivo es su propio ámbito, las dependencias son imports explícitos y los bundlers pueden descartar los exports que no se usan. TypeScript 7 eliminó outFile (error TS5102), así que la configuración de namespaces repartidos en varios archivos ya no es una opción de compilación.

NamespaceMódulo
Unidadun bloque con nombre dentro de un archivoel propio archivo
Ámbitoglobal salvo que esté dentro de un módulosiempre el suyo
Dependenciasimplícitas, por orden de cargaimport explícito
Salidaun objeto construido por una funciónimport/export o require
Eliminación de código no usadolos bundlers conservan todos los miembroslos bundlers pueden descartar exports no usados
Funciona con el type stripping de Nodesolo si contiene únicamente tipossí

Dentro de un módulo, envolverlo todo en un namespace añade un segundo nivel de nombres sin ganar nada: quien importe escribiría Utils.Utils.format. Exporta las funciones directamente y deja que quien importe elija import * as Utils from "./utils.js" si quiere un prefijo.

Dónde siguen apareciendo los namespaces

Los archivos de declaraciones los usan para describir librerías que exponen un único objeto global, y para agrupar tipos:

// jquery-like.d.ts: a global function that also has properties
declare function $(selector: string): unknown;
declare namespace $ {
    const version: string;
    function ajax(url: string): Promise<unknown>;
}

Los paquetes de tipos los usan para exponer tipos que puedes ampliar. @types/node declara namespace NodeJS, y ampliar su interfaz ProcessEnv desde cualquier módulo requiere declare global:

// env.d.ts
export {};

declare global {
    namespace NodeJS {
        interface ProcessEnv {
            API_URL: string; // process.env.API_URL is now string, not string | undefined
        }
    }
}

Las interfaces dentro de namespaces fusionados también se fusionan, y eso es lo que hace funcionar esta ampliación.

Namespaces y type stripping

Node 24 ejecuta archivos .ts borrando la sintaxis de tipos. Un namespace con valores no se puede borrar, hay que compilarlo al objeto que se ha mostrado arriba, así que node app.ts se detiene con:

SyntaxError [ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX]: TypeScript namespace declaration is not supported in strip-only mode

Hay dos tipos de namespace que funcionan porque desaparecen por completo: un declare namespace, y un namespace cuyos miembros son todos tipos o interfaces. La opción del compilador erasableSyntaxOnly: true marca los demás en tiempo de compilación con el error TS1294, This syntax is not allowed when 'erasableSyntaxOnly' is enabled., y así es como los proyectos que se ejecutan con type stripping los mantienen fuera. node --experimental-transform-types sí compila namespaces, con un aviso de que es experimental.

Preguntas frecuentes

¿Uso namespaces o módulos en TypeScript?

Usa módulos (import y export) en código nuevo. Cada archivo ya es su propio ámbito, los bundlers y Node entienden los módulos, y los exports que no se usan se pueden eliminar. Los namespaces siguen siendo útiles en archivos de declaraciones, para ampliar el ámbito global y para añadir tipos o funciones auxiliares a una función o clase con el mismo nombre.

¿A qué compila un namespace de TypeScript?

A un objeto que rellena una función invocada inmediatamente: var Geometry; (function (Geometry) { Geometry.circle = circle; })(Geometry || (Geometry = {}));. Los miembros exportados pasan a ser propiedades de ese objeto; los que no llevan export se quedan como locales de la función.

¿Qué diferencia hay entre namespace y module en TypeScript?

Un módulo es un archivo con un import o export de nivel superior. Un namespace es un bloque con nombre dentro de un archivo. El TypeScript antiguo llamaba a los namespaces "internal modules" y permitía module Foo {}; esa forma ahora es el error TS1540, y solo se acepta namespace Foo {}.

¿Puede Node ejecutar archivos TypeScript que usan namespaces?

No con su type stripping por defecto. Un namespace que contiene valores produce ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX, porque el stripping solo borra tipos y un namespace necesita código generado. Los namespaces que solo contienen tipos, y declare namespace, se borran y funcionan sin problema.

Coddy programming languages illustration

Aprende a programar con Coddy

COMENZAR