Menu

Namespace em TypeScript: o que é e quando usar

Um namespace em TypeScript agrupa valores e tipos sob um único nome e vira um objeto comum na compilação. Veja a sintaxe, como namespaces se mesclam entre si e com funções e classes, por que os ES modules os substituíram e onde eles ainda aparecem: arquivos de declaração e global augmentation.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

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.

NamespaceMódulo
Unidadeum bloco nomeado em um arquivoo próprio arquivo
Escopoglobal, a menos que esteja dentro de um módulosempre o seu próprio
Dependênciasimplícitas, pela ordem de carregamentoimport explícito
Saídaum objeto montado por uma funçãoimport/export ou require
Remoção de código não usadobundlers mantêm todos os membrosbundlers podem descartar exports não usados
Roda com type stripping do Nodesó se contiver apenas tipossim

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.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR