Módulos e namespaces

1. Fundamentos de Módulos no TypeScript

Módulos no TypeScript representam a forma moderna de organizar e compartilhar código entre arquivos. Diferentemente de scripts tradicionais, módulos possuem seu próprio escopo, evitando poluição do escopo global e permitindo controle explícito sobre o que é exposto ou consumido externamente.

O TypeScript segue o sistema de módulos ES (ECMAScript Modules) como padrão, mas também oferece suporte a outros formatos como CommonJS, AMD e UMD, configuráveis via tsconfig.json. A sintaxe fundamental utiliza export para expor membros e import para consumi-los:

// utils/matematica.ts
export function somar(a: number, b: number): number {
  return a + b;
}

export const PI = 3.14159;

// app.ts
import { somar, PI } from './utils/matematica';
console.log(somar(5, 3)); // 8

Reexportações permitem consolidar múltiplos módulos em um ponto central:

// utils/index.ts
export { somar, PI } from './matematica';
export { formatarData } from './datas';

Módulos internos vs. externos: Historicamente, o TypeScript chamava namespaces de "módulos internos" e arquivos com import/export de "módulos externos". Hoje, a terminologia evoluiu: módulos referem-se exclusivamente a arquivos com importações/exportações, enquanto namespaces são uma construção separada.

2. Módulos ES: Sintaxe Avançada

Exportações nomeadas vs. exportação default:

// exportação nomeada (múltiplas por módulo)
export function validarEmail(email: string): boolean { ... }
export class Usuario { ... }

// exportação default (uma por módulo)
export default class Configuracao { ... }

Importações com alias e namespace:

// alias para evitar conflitos
import { Usuario as User } from './models';
import { default as Config } from './config';

// importação de namespace (agrupa todas exportações)
import * as Utilitarios from './utils';
Utilitarios.somar(2, 3);

Dynamic imports com suporte a tipos:

async function carregarModuloPesado() {
  const modulo = await import('./modulo-pesado');
  const resultado = modulo.executar();
  return resultado;
}

O TypeScript preserva a tipagem mesmo em imports dinâmicos, retornando uma Promise do tipo do módulo importado.

3. Namespaces: Organização Interna de Código

Namespaces são uma forma de agrupar código relacionado dentro de um escopo nomeado, evitando conflitos globais. Eram a principal forma de organização antes dos módulos ES se tornarem padrão.

namespace Validacao {
  export function email(valor: string): boolean {
    return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(valor);
  }

  export function cpf(valor: string): boolean {
    return valor.length === 11;
  }

  namespace Interno {
    export function sanitizar(texto: string): string {
      return texto.trim().toLowerCase();
    }
  }
}

// Uso com nome qualificado
Validacao.email('teste@exemplo.com'); // true
Validacao.Interno.sanitizar('  TESTE  '); // 'teste'

Namespaces podem ser divididos em múltiplos arquivos usando /// <reference>:

// validacao.ts
namespace Validacao {
  export function email(valor: string): boolean { ... }
}

// validacao-cpf.ts
/// <reference path="validacao.ts" />
namespace Validacao {
  export function cpf(valor: string): boolean { ... }
}

4. Módulos vs. Namespaces: Quando Usar Cada Um

Vantagens dos módulos:
- Isolamento completo de escopo
- Suporte nativo a tree-shaking (eliminação de código morto)
- Interoperabilidade com ecossistema JavaScript moderno
- Carregamento assíncrono via dynamic imports
- Melhor integração com bundlers (Webpack, Rollup, Vite)

Casos de uso para namespaces:
- Scripts legados que não utilizam sistema de módulos
- Declarações de tipos globais em arquivos .d.ts
- Ambientes sem suporte a ES Modules (ex: scripts no navegador sem bundler)

Boas práticas: Em projetos modernos, prefira módulos. Namespaces são considerados obsoletos para novos desenvolvimentos, exceto em cenários específicos de declaração de tipos.

5. Módulos no Sistema de Módulos do TypeScript

O tsconfig.json oferece configurações cruciais para o comportamento de módulos:

{
  "compilerOptions": {
    "module": "ESNext",
    "moduleResolution": "node",
    "baseUrl": "./src",
    "paths": {
      "@models/*": ["models/*"],
      "@utils/*": ["utils/*"]
    }
  }
}

Opções de module:
- ESNext/ES2020 — para projetos modernos com bundlers
- CommonJS — para Node.js tradicional
- AMD — para carregamento assíncrono no navegador
- UMD — para bibliotecas que funcionam em múltiplos ambientes

Path mapping permite criar aliases elegantes:

// Em vez de: import { Usuario } from '../../models/usuario'
import { Usuario } from '@models/usuario';

6. Namespaces e Declarações de Tipos (Ambient Modules)

Para descrever bibliotecas JavaScript sem tipos, usamos declarações ambientes:

// types/minha-biblioteca.d.ts
declare module 'minha-biblioteca' {
  export function fazerAlgo(param: string): number;
  export class Cliente {
    constructor(nome: string);
    getNome(): string;
  }
}

Namespaces em arquivos .d.ts são comuns para bibliotecas globais:

// types/jquery.d.ts
declare namespace $ {
  function ajax(config: object): void;
  function (seletor: string): JQueryElement;

  interface JQueryElement {
    hide(): void;
    show(): void;
  }
}

7. Interoperabilidade entre Módulos e Namespaces

É possível exportar namespaces a partir de módulos:

// formas.ts
export namespace Formas {
  export class Circulo {
    constructor(public raio: number) {}
    area(): number {
      return Math.PI * this.raio ** 2;
    }
  }
}

// app.ts
import { Formas } from './formas';
const circulo = new Formas.Circulo(5);

Para migração gradual de namespaces para módulos, mantenha ambos funcionando durante a transição:

// antigo: namespace MeuApp { ... }
// novo:
export namespace MeuApp {
  export function funcaoAntiga() { ... }
}

// arquivo novo
export function funcaoNova() { ... }

Conclusão

Módulos são o padrão moderno para organização de código em TypeScript, oferecendo isolamento, interoperabilidade e suporte a técnicas avançadas como lazy loading. Namespaces ainda têm seu lugar em cenários específicos, especialmente em declarações de tipos e código legado, mas devem ser evitados em projetos novos. A escolha entre um e outro depende do contexto, mas a tendência clara é a adoção de módulos ES como padrão universal.

Referências