Um namespace é um bloco nomeado que agrupa funções, constantes e tipos sob um único nome. Os membros marcados com export são acessíveis como Name.member; o resto fica privado ao bloco. Em tempo de execução, um namespace é um objeto comum.
Em que um namespace é compilado
Namespaces são um dos poucos recursos do TypeScript que geram código. O bloco acima vira uma função que preenche um 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 = {}));
Esse é o padrão que o código JavaScript usava antes de existirem módulos, para não colocar todos os nomes no escopo global. TAU é uma variável local da função, por isso outro código não consegue enxergá-la. Tentar lê-la é erro de compilação:
O compilador mostra index.ts(10,17): error TS2339: Property 'rate' does not exist on type 'typeof Tax'. Adicione export antes de const rate e as duas linhas rodam.
Aninhamento, merge e aliases
Namespaces podem ser aninhados, e dois blocos com o mesmo nome se mesclam em um só. O Geometry || (Geometry = {}) na saída é o que faz isso funcionar: o segundo bloco adiciona membros ao objeto existente. import X = A.B cria um alias curto.
namespace A.B.C { } é uma forma curta de três blocos aninhados. A grafia antiga module Shop { } significa a mesma coisa, mas agora é rejeitada com o erro TS1540, A 'namespace' declaration should not be declared using the 'module' keyword. Please use the 'namespace' keyword instead.
Merge com funções e classes
Um namespace pode ter o mesmo nome de uma função, classe ou enum e adicionar membros a ela. Essa ainda é a forma mais limpa de descrever uma função que também tem propriedades, ou uma classe com funções auxiliares anexadas.
Em uma classe, um método static faz o mesmo trabalho e é JavaScript puro. Em uma função, você também pode dispensar o namespace e atribuir format.prefix = "$" logo depois da declaração; o TypeScript acompanha propriedades atribuídas dessa forma.
Namespaces vs módulos
Antes dos ES modules, um programa TypeScript grande era um monte de arquivos de script compartilhando namespaces globais, ligados com /// <reference path="..." /> e compilados em um único arquivo com outFile. Os módulos substituíram isso: cada arquivo é seu próprio escopo, as dependências são imports explícitos e os bundlers podem descartar exports não usados. O TypeScript 7 removeu outFile (erro TS5102), então a configuração com namespaces em vários arquivos deixou de ser uma opção de build.
| Namespace | Módulo | |
|---|---|---|
| Unidade | um bloco nomeado em um arquivo | o próprio arquivo |
| Escopo | global, a menos que esteja dentro de um módulo | sempre o seu próprio |
| Dependências | implícitas, pela ordem de carregamento | import explícito |
| Saída | um objeto montado por uma função | import/export ou require |
| Remoção de código não usado | bundlers mantêm todos os membros | bundlers podem descartar exports não usados |
| Roda com type stripping do Node | só se contiver apenas tipos | sim |
Dentro de um módulo, envolver tudo em um namespace acrescenta um segundo nível de nomes sem ganho nenhum: quem importa escreveria Utils.Utils.format. Exporte as funções diretamente e deixe quem importa escolher import * as Utils from "./utils.js" se quiser um prefixo.
Onde namespaces ainda aparecem
Arquivos de declaração os usam para descrever bibliotecas que expõem um único objeto global e 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>;
}
Pacotes de tipos os usam para expor tipos que você pode estender. @types/node declara namespace NodeJS, e adicionar algo à interface ProcessEnv dele a partir de qualquer módulo exige 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
}
}
}
Interfaces dentro de namespaces mesclados também se mesclam, e é isso que faz essa augmentation funcionar.
Namespaces e type stripping
O Node 24 roda arquivos .ts apagando a sintaxe de tipos. Um namespace com valores não pode ser apagado, ele precisa ser compilado no objeto mostrado acima, então node app.ts para com:
SyntaxError [ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX]: TypeScript namespace declaration is not supported in strip-only mode
Dois tipos de namespace funcionam porque somem por completo: um declare namespace e um namespace cujos membros são todos tipos ou interfaces. A opção de compilador erasableSyntaxOnly: true aponta os outros em tempo de compilação como o erro TS1294, This syntax is not allowed when 'erasableSyntaxOnly' is enabled., e é assim que projetos que rodam com type stripping os mantêm de fora. node --experimental-transform-types compila namespaces, com um aviso de recurso experimental.
Perguntas frequentes
Devo usar namespaces ou módulos no TypeScript?
Use módulos (import e export) em código novo. Cada arquivo já é seu próprio escopo, bundlers e o Node entendem módulos, e exports não usados podem ser removidos. Namespaces continuam úteis em arquivos de declaração, para global augmentation e para anexar tipos ou funções auxiliares a uma função ou classe com o mesmo nome.
Em que um namespace do TypeScript é compilado?
Em um objeto preenchido por uma função executada imediatamente: var Geometry; (function (Geometry) { Geometry.circle = circle; })(Geometry || (Geometry = {}));. Os membros exportados viram propriedades desse objeto; os membros sem export ficam locais à função.
Qual é a diferença entre namespace e module no TypeScript?
Um módulo é um arquivo com import ou export no nível superior. Um namespace é um bloco nomeado dentro de um arquivo. O TypeScript antigo chamava namespaces de "internal modules" e aceitava module Foo {}; essa grafia agora é o erro TS1540, e só namespace Foo {} é aceito.
O Node consegue rodar arquivos TypeScript que usam namespaces?
Não com o type stripping padrão. Um namespace que contém valores gera ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX, porque o stripping só apaga tipos e um namespace precisa de código gerado. Namespaces que só contêm tipos, e declare namespace, são apagados e rodam sem problema.