Aprenda o que é o padrão Singleton, quando ele faz sentido e como implementá‑lo corretamente em TypeScript. Veja exemplos práticos, riscos para testes e alternativas mais testáveis, como injeção de dependência.
January 19, 2026 (3mo ago) — last updated April 20, 2026 (11d ago)
Singleton em TypeScript: Guia Completo
Aprenda quando usar Singleton, exemplos em TypeScript, riscos para testes e alternativas como injeção de dependência para código mais testável.
← Back to blog
Dominando o Padrão Singleton em TypeScript: Um Guia Completo

No mundo do desenvolvimento de software, algumas ferramentas são poderosas, mas precisam ser manuseadas com cuidado. O padrão Singleton é uma delas. Em sua essência, é um conceito simples: garantir que uma classe tenha apenas uma instância e fornecer uma maneira única e global de acessá‑la.1
Pense nisso como um gerenciador de configuração central ou um serviço de logging dedicado para toda a sua aplicação. Você não gostaria de ter vários objetos de configuração conflitantes circulando por aí, nem entradas de log espalhadas por diferentes arquivos por instâncias de logger em competição. O Singleton traz ordem ao impedir essas duplicações, economizando memória e evitando o caos. É um padrão fundamental para gerenciar o acesso a recursos compartilhados.
TypeScript é uma escolha comum para implementar Singletons em aplicações modernas, já que seus modificadores de acesso ajudam a reforçar contratos de criação de instância e segurança de tipos26.
O que é o Padrão Singleton e Quando Ele é Útil?
Imagine um reino medieval com apenas um Escriba Real oficial. Essa pessoa é a única autorizada a registrar decretos reais. Isso garante que toda lei e anúncio seja consistente, devidamente autorizado e armazenado em um único registro definitivo. Se qualquer um pudesse simplesmente decidir ser escriba, o reino rapidamente cairia no caos com registros contraditórios e confusão em massa.
O padrão de projeto Singleton opera exatamente com esse princípio dentro do seu software. Sua principal função é restringir uma classe para que apenas um único objeto jamais possa ser criado a partir dela. Essa instância única torna‑se a fonte da verdade para uma tarefa específica e é facilmente acessível de qualquer lugar da sua base de código. É assim que você impõe controle sobre recursos que nunca devem ser duplicados.
Propósito central e analogia
O Singleton não é apenas sobre impedir que as pessoas criem novos objetos; é sobre centralizar o controle. Assim como o Escriba Real fornece um único ponto de acesso aos registros oficiais do reino, uma instância Singleton oferece um portal universalmente disponível para um recurso compartilhado. Ele impede que diferentes partes da sua aplicação criem suas próprias versões isoladas e potencialmente conflitantes.
Um pool de conexões com o banco de dados é um exemplo clássico. Você definitivamente não quer que cada componente do seu app abra sua própria conexão separada ao banco; isso pode esgotar os recursos do servidor e degradar o desempenho. Em vez disso, um Singleton pode gerenciar um único pool de conexões, distribuindo‑as de forma eficiente conforme necessário.
A ideia central é simples, porém poderosa: uma classe, uma instância, um ponto de acesso global. Essa estrutura garante que todas as interações com um recurso específico passem por um único canal controlado.
Padrão Singleton em resumo
| Característica | Descrição & justificativa |
|---|---|
| Instância única | A classe é projetada para ter apenas uma instância durante o ciclo de vida da aplicação, frequentemente aplicada com um construtor privado. |
| Ponto de acesso global | Um método estático (por exemplo, getInstance()) fornece uma forma única e conhecida de acessar a instância de qualquer lugar do código. |
| Inicialização preguiçosa | A instância única costuma ser criada na primeira vez que é requisitada, não na inicialização da aplicação, o que pode melhorar o desempenho. |
| Gerenciamento de estado | Atua como um local centralizado para um pedaço específico de estado global, como configurações de aplicação ou sessão do usuário. |
Esta tabela resume por que o padrão existe: impor uma instância única e globalmente acessível para recursos que são, por natureza, singulares.
Casos de uso práticos
Embora o padrão Singleton tenha seus críticos, ele não é desprovido de usos legítimos. É mais eficaz quando você tem um recurso que é, por sua própria natureza, único dentro do sistema.
Aqui estão alguns cenários onde um Singleton faz sentido:
- Serviços de logging: uma única instância de logger garante que todos os eventos fluam para o mesmo arquivo ou stream.
- Gerenciamento de configuração: uma fonte única para configurações da aplicação evita inconsistências entre módulos.
- Acesso a interface de hardware: uma interface única a um dispositivo evita comandos conflitantes.
Benefícios e desvantagens de usar Singletons

O padrão Singleton pode parecer uma ferramenta confiável quando você precisa de um ponto único de acesso a um recurso compartilhado. Ele oferece uma forma direta de gerenciar coisas como um objeto de configuração ou um serviço de logging em toda a sua aplicação.
Benefícios dos Singletons
- Ponto de acesso global simplifica o uso entre módulos.
- Conservação de recursos por meio de inicialização preguiçosa pode reduzir custo de inicialização.
- Redução de duplicação evita múltiplas instâncias conflitantes de recursos caros.
Desvantagens dos Singletons
- Acoplamento forte: classes podem esconder dependências ao acessar estado global.
- Estado global: estado mutável compartilhado pode causar bugs difíceis de encontrar.
- Efeitos colaterais ocultos: métodos que dependem de um Singleton não expõem essa dependência em suas assinaturas, dificultando raciocínio e testes.
Impacto nos testes e acoplamento
Singletons complicam testes unitários porque introduzem estado global e persistente. Testes correm o risco de vazar estado entre execuções, e fazer mock de um Singleton pode se tornar desconfortável. Times modernos frequentemente preferem injeção de dependência porque torna as dependências explícitas e fáceis de substituir durante os testes.3
Balanceando os trade‑offs
Ao decidir se deve usar um Singleton, pese a conveniência contra a manutenibilidade e testabilidade a longo prazo. Para bases de código legadas, refatorações incrementais em direção à injeção de dependência costumam ser a rota mais segura: mantenha o comportamento enquanto reduz o acoplamento oculto e melhora a testabilidade.
Singletons trocam simplicidade por estado global, então escolha com sabedoria com base nas necessidades da sua equipe.
Principais conclusões
- Use Singletons com parcimônia e apenas para serviços que realmente devem ser únicos.
- Prefira injeção de dependência explícita para melhor desacoplamento e testabilidade.
- Se precisar usar um Singleton, faça‑o lazy e esteja atento à concorrência e segurança de threads.
- Para sistemas legados, elimine Singletons gradualmente introduzindo interfaces e DI na raiz de composição.
Como implementar o padrão Singleton em TypeScript

Vamos sair do abstrato para o prático e construir um Singleton moderno e com segurança de tipos em TypeScript. O ingrediente secreto é um construtor private e um método static que atua como porteiro. Essa combinação garante que nenhuma outra parte da sua aplicação possa criar uma nova instância; todos passam pelo único ponto de entrada.2
Para o nosso exemplo prático, vamos criar um ConfigManager. Essa classe carrega e fornece configurações da aplicação, garantindo que todo componente leia da mesma fonte da verdade.
Construindo um ConfigManager com tipos
// A practical example of the Singleton pattern for configuration management.
class ConfigManager {
// 1. A private, static property to hold the single instance.
private static instance: ConfigManager;
// 2. A place to store our configuration data.
private settings: Map<string, any> = new Map();
// 3. The private constructor. This stops `new ConfigManager()` from working anywhere else.
private constructor() {
// In a real app, you'd load from a file, environment variables, or a service.
console.log("Initializing ConfigManager instance...");
this.settings.set("API_URL", "https://api.example.com");
this.settings.set("TIMEOUT", 5000);
}
// 4. The public, static method that controls access to the single instance.
public static getInstance(): ConfigManager {
if (!ConfigManager.instance) {
ConfigManager.instance = new ConfigManager();
}
return ConfigManager.instance;
}
// 5. A regular public method to get a specific setting.
public get(key: string): any {
return this.settings.get(key);
}
}
// TypeScript will stop you from doing this:
// const config = new ConfigManager(); // Error: Constructor of class 'ConfigManager' is private.
Esta estrutura usa os modificadores de acesso do TypeScript para impor uma classe de instância única e inicialização preguiçosa. O construtor private e o padrão de instância static são diretos e eficazes para muitas necessidades simples.
Colocando o Singleton para funcionar em um serviço
class ApiService {
private apiUrl: string;
constructor() {
const config = ConfigManager.getInstance();
this.apiUrl = config.get("API_URL");
console.log(`ApiService initialized with API URL: ${this.apiUrl}`);
}
public fetchData(): void {
console.log(`Fetching data from ${this.apiUrl}...`);
// Real data-fetching logic would live here.
}
}
// --- Application Entry Point ---
console.log("Application starting...");
const service1 = new ApiService();
service1.fetchData();
const service2 = new ApiService();
console.log("Application finished.");
Quando você rodar esse código, deve ver a mensagem de inicialização do ConfigManager apenas uma vez, provando que ambos os serviços receberam a mesma instância.
Por que Singletons têm má reputação
O padrão Singleton parece atraente porque é simples e fornece um objeto acessível globalmente. O que pode dar errado é o acoplamento oculto, estado global mutável e pesadelos de testes. Quando uma classe silenciosamente busca uma instância global, ela esconde uma dependência que deveria ser explícita em seu construtor. Isso torna o sistema mais difícil de entender e de testar.3
Pesadelos em concorrência e estado global
Singletons com estado podem causar condições de corrida em cenários concorrentes. Considere um SessionCounter onde duas requisições simultâneas incrementam o mesmo contador. Sem sincronização, ambas podem ler o mesmo valor inicial e gravar atualizações conflitantes. Esses bugs dependem do timing e são difíceis de reproduzir.
O dilema dos testes
Singletons tornam testes unitários frágeis porque o estado pode vazar entre testes e o mocking fica difícil. Testes podem começar a depender da ordem de execução e a suíte tornar‑se instável. Por isso times frequentemente adotam injeção de dependência: ela torna as dependências explícitas e fáceis de simular.3
Apesar desses problemas, Singletons persistem por aí. Eles costumam se espalhar numa base de código uma vez introduzidos, por isso auditorias e refatorações incrementais são importantes ao melhorar a arquitetura.
Alternativas modernas ao padrão Singleton
Depois de ver os riscos que os Singletons introduzem, você provavelmente está se perguntando: “O que devo usar em vez disso?” Injeção de dependência (DI) é a abordagem preferida para gerenciar recursos compartilhados. DI torna as dependências explícitas e melhora a testabilidade e modularidade.
Injeção de Dependência vs Singletons
Compare o ApiService original, que força o uso de um Singleton, com uma versão que aceita um gerenciador de configuração via seu construtor.
interface IConfigManager {
get(key: string): any;
}
class ApiService {
private apiUrl: string;
constructor(config: IConfigManager) {
this.apiUrl = config.get("API_URL");
}
}
Agora o ApiService depende apenas do contrato IConfigManager. Durante os testes você pode passar um fake ou mock, tornando os testes rápidos e previsíveis.
Ao inverter o controle sobre quem cria as dependências, os componentes ficam mais focados e flexíveis. Essa ideia é central ao Princípio da Inversão de Dependência.
O papel dos contêineres IoC
Um contêiner de Inversão de Controle (IoC) gerencia a criação de objetos e a injeção para sua aplicação. Frameworks populares em TypeScript fornecem contêineres de DI embutidos, como NestJS e Angular, ou bibliotecas como InversifyJS para projetos gerais.45
Contêineres permitem que você escolha como os objetos são compartilhados: com ciclos de vida transitórios, por escopo ou semelhantes a singletons. Isso oferece os benefícios de uma instância compartilhada sem o acoplamento oculto de um Singleton programático.
Como refatorar Singletons em uma base de código legada
Trabalhe de forma incremental. Identifique onde o Singleton é usado, defina uma interface clara que descreva seu comportamento e comece a mudar consumidores individuais para aceitar a interface via injeção no construtor. Depois, conecte a instância concreta na raiz de composição ou deixe um contêiner de DI gerenciá‑la.
Passo 1: Identificar e isolar o Singleton
Encontre todas as chamadas a MySingleton.getInstance() e desenhe um limite ao redor das responsabilidades do Singleton. Defina uma interface que liste os métodos públicos que você precisa.
Passo 2: Introduzir injeção de dependência incrementalmente
Refatore um consumidor de cada vez:
- Altere o construtor para aceitar a interface.
- Substitua chamadas diretas a
getInstance()por chamadas à instância injetada. - No ponto de instanciação, passe a instância do Singleton até que a migração completa esteja finalizada.
Isso mantém a aplicação estável enquanto você reduz o acoplamento oculto.
Passo 3: Substituir o Singleton por uma instância gerenciada
Uma vez que os consumidores aceitem dependências via interface, você pode remover o getInstance() estático e transformar a implementação em uma classe comum com construtor público. Crie uma instância na raiz de composição e passe‑a onde for necessário, ou deixe um contêiner de DI gerenciar o ciclo de vida e o escopo.
Respondendo suas perguntas urgentes sobre Singletons
Singletons são sempre uma má ideia?
Não sempre. Eles podem fazer sentido para serviços verdadeiramente únicos e sem estado, como um logger centralizado ou um adaptador de hardware. Ainda assim, DI e uma raiz de composição controlada frequentemente oferecem o mesmo comportamento com melhor testabilidade.
Como Singletons estragam os testes unitários?
Eles introduzem estado global e persistente que pode vazar entre testes e dificultar o mocking. Testes podem se tornar dependentes da ordem e instáveis. DI simplifica os testes porque mocks podem ser injetados diretamente.
Uma classe estática não é basicamente a mesma coisa?
Não. Uma classe estática apenas hospeda membros estáticos e não pode ser instanciada. Um Singleton tem uma instância real e pode implementar interfaces e ser passada como objeto. Ambas as abordagens podem levar a acoplamento forte, então prefira DI para flexibilidade.
Próximos passos para sua equipe
Inicie uma conversa sobre onde, se é que existe, uma instância compartilhada única é realmente necessária. Prototipe DI em um módulo isolado, adote padrões de codificação claros e use ferramentas para medir dívida técnica. Programação em par e feedback contínuo ajudam a manter refatorações seguras e eficientes.
Lembre‑se: nenhum padrão de projeto é uma bala de prata. Singletons têm seu lugar, mas devem ser usados com critério e acompanhados de interfaces limpas e regras claras de propriedade.
Perguntas rápidas (Q&A)
P: Quando devo optar por um Singleton em vez de DI? A: Quando o recurso for verdadeiramente único no sistema e sem estado crítico, e quando a complexidade de um contêiner DI não compensa a solução simples do Singleton.
P: Qual a melhor prática para testar código que usa Singletons hoje? A: Introduza uma interface, passe dependências via construtor e injete um mock nas suites de teste. Faça a migração de forma incremental para evitar regressões.
P: Como minimizar riscos de concorrência ao usar Singletons? A: Garanta sincronização adequada ao acessar estado compartilhado, prefira objetos imutáveis quando possível, e documente claramente os pontos de acesso.
IA escreve código.Você faz durar.
Na era da aceleração da IA, código limpo não é apenas uma boa prática — é a diferença entre sistemas que escalam e bases de código que entram em colapso sob seu próprio peso.