January 19, 2026 (7mo ago) — last updated July 1, 2026 (1mo ago)

Singleton en TypeScript: guía práctica y alternativas

Guía práctica del patrón Singleton en TypeScript: cuándo usarlo, implementación segura, riesgos, alternativas con inyección de dependencias y consejos para tests.

← Back to blog
Cover Image for Singleton en TypeScript: guía práctica y alternativas

En el desarrollo de software, algunas soluciones son poderosas pero requieren cuidado. El patrón Singleton garantiza una única instancia y un punto de acceso global, ideal para recursos como gestores de configuración o logging. En esta guía aprenderás cuándo usarlo, cómo implementarlo de forma segura en TypeScript, sus riesgos y alternativas con inyección de dependencias.

Patrón Singleton en TypeScript: Una guía completa

Un boceto a lápiz de un escriba real que lleva una corona, examinando diligentemente un pergamino brillante sobre una mesa.

En el desarrollo de software, algunas soluciones son poderosas pero requieren cuidado. El patrón Singleton garantiza que una clase tenga una única instancia y proporciona un punto de acceso global a esa instancia, útil para recursos como gestores de configuración o servicios de logging1. En esta guía verás cuándo usarlo, cómo implementarlo en TypeScript, sus riesgos, y alternativas modernas con inyección de dependencias.

¿Qué es el patrón Singleton y cuándo es útil?

El patrón Singleton restringe la creación de objetos para que solo exista una instancia durante el ciclo de vida de la aplicación. Esa instancia central actúa como la fuente de verdad para una responsabilidad específica, como configuración o acceso a hardware.

Propósito central y analogía

No se trata solo de impedir nuevas instancias; se trata de centralizar el control. Igual que el Escritor Real gestiona los decretos, un Singleton centraliza un recurso compartido para evitar versiones conflictivas.

Un uso típico es la gestión de conexiones a la base de datos o un pool de conexiones: no quieres que cada componente abra su propia conexión; un único gestor puede repartir las conexiones de forma eficiente.

Patrón Singleton de un vistazo

CaracterísticaDescripción
Instancia únicaLa clase se diseña para tener solo una instancia (constructor privado).
Punto de acceso globalUn método estático (por ejemplo, getInstance()) devuelve la instancia desde cualquier parte del código.
Inicialización perezosaLa instancia suele crearse la primera vez que se solicita, reduciendo el coste de arranque.
Gestión de estadoCentraliza estado compartido, como configuración o sesión.

Casos de uso prácticos

Cuando el recurso es inherentemente único, el Singleton puede ser apropiado:

  • Servicios de logging: una única instancia central evita fragmentación de registros.
  • Gestión de configuración: una única fuente de ajustes para toda la aplicación.
  • Acceso a hardware: una interfaz única a un dispositivo físico evita comandos en conflicto.

Beneficios y desventajas de usar Singletons

Una balanza comparando patrones de diseño estructurados y observables con código complejo, enmarañado y experimental.

El Singleton es directo y útil para ciertos problemas, pero también trae compromisos importantes.

Beneficios

  • Punto de acceso global que simplifica llamadas entre módulos.
  • Ahorro de recursos mediante inicialización perezosa.
  • Evita múltiples instancias conflictivas de recursos costosos.

Desventajas

  • Acoplamiento fuerte: las dependencias quedan ocultas y el código puede volverse rígido.
  • Estado global mutable: puede causar errores difíciles de reproducir.
  • Impacto en las pruebas: el estado persistente complica el aislamiento y el mocking.3

Impacto en las pruebas y el acoplamiento

Los Singletons complican las pruebas unitarias al introducir estado global que puede filtrarse entre pruebas. Por eso muchos equipos prefieren la inyección de dependencias (DI), que hace las dependencias explícitas y fáciles de sustituir en tests3.

Equilibrando los compromisos

Valora conveniencia frente a mantenibilidad. En código legacy, una ruta segura es refactorizar de forma incremental hacia DI, introduciendo interfaces y pasando dependencias desde la raíz de composición.

Usa Singletons con moderación: simplicidad hoy puede significar deuda técnica mañana.

Implementación del patrón Singleton en TypeScript

Diagrama que muestra ConfigManager con un candado, demostrando el patrón de diseño Singleton usando getInstance en TypeScript.

A continuación, un ejemplo claro y tipado en TypeScript. La clave es un constructor private y un método static que controla la instancia.2

Ejemplo: ConfigManager con tipos seguros

class ConfigManager {
  private static instance: ConfigManager;
  private settings: Map<string, any> = new Map();

  private constructor() {
    console.log('Inicializando ConfigManager...');
    this.settings.set('API_URL', 'https://api.example.com');
    this.settings.set('TIMEOUT', 5000);
  }

  public static getInstance(): ConfigManager {
    if (!ConfigManager.instance) {
      ConfigManager.instance = new ConfigManager();
    }
    return ConfigManager.instance;
  }

  public get(key: string): any {
    return this.settings.get(key);
  }
}

// const config = new ConfigManager(); // Error: el constructor es privado

Este patrón aplica los modificadores de TypeScript para asegurar una instancia única e inicialización perezosa.2

Usando el Singleton en un servicio

class ApiService {
  private apiUrl: string;

  constructor() {
    const config = ConfigManager.getInstance();
    this.apiUrl = config.get('API_URL');
    console.log(`ApiService inicializado con API URL: ${this.apiUrl}`);
  }

  public fetchData(): void {
    console.log(`Consultando ${this.apiUrl}...`);
    // Lógica real de fetch aquí.
  }
}

console.log('Aplicación iniciando...');
const service1 = new ApiService();
service1.fetchData();
const service2 = new ApiService();
console.log('Aplicación finalizada.');

Al ejecutar, verás el mensaje de inicialización de ConfigManager solo una vez, lo que demuestra que ambas instancias usan la misma configuración.

Por qué los Singletons tienen mala fama

Aunque atractivos por su simplicidad, los Singletons ocultan dependencias, introducen estado compartido y complican las pruebas. Cuando una clase accede silenciosamente a una instancia global, esa dependencia debería ser explícita en su constructor para facilitar el razonamiento y el testing.3

Condiciones de carrera y concurrencia

Los Singletons con estado pueden sufrir condiciones de carrera en entornos concurrentes. Por ejemplo, un contador de sesión incrementado por peticiones simultáneas puede producir resultados inconsistentes sin sincronización adecuada.

Pruebas frágiles

El estado persistente de un Singleton puede filtrarse entre pruebas y hacer que el orden de ejecución influya en los resultados. La inyección de dependencias permite pasar dobles (mocks) fácilmente y aísla cada prueba. Para más sobre testing y patrones ver: Testing en TypeScript y Buenas prácticas de DI.

Alternativas modernas al Singleton

La inyección de dependencias (DI) es la alternativa preferida para gestionar recursos compartidos. DI hace las dependencias explícitas y mejora la modularidad y la testabilidad.

Inyección de dependencias frente a Singletons

En lugar de que ApiService llame a ConfigManager.getInstance(), acepta un gestor de configuración por constructor:

interface IConfigManager {
  get(key: string): any;
}

class ApiService {
  private apiUrl: string;

  constructor(config: IConfigManager) {
    this.apiUrl = config.get('API_URL');
  }
}

Ahora ApiService depende del contrato IConfigManager. En tests puedes pasar un mock, y el acoplamiento queda explícito.

Algunos frameworks de TypeScript incluyen contenedores DI integrados (por ejemplo, NestJS) o permiten usar bibliotecas como InversifyJS para gestionar ciclos de vida y alcances de objetos45.

Contenedores IoC

Un contenedor de Inversión de Control permite decidir si una dependencia es transitoria, por alcance o compartida a nivel de aplicación. Esto ofrece los beneficios de una instancia compartida sin el acoplamiento oculto de un Singleton programático.

Refactorizar Singletons en código legacy

Trabaja de forma incremental: identifica llamadas a MySingleton.getInstance(), define una interfaz y refactoriza consumidores uno por uno para aceptar la interfaz por constructor. Mantén la instancia concreta en la raíz de composición hasta que completes la migración.

Pasos recomendados

  1. Detecta y aísla las dependencias del Singleton.
  2. Define una interfaz con los métodos públicos necesarios.
  3. Refactoriza consumidores para aceptar la interfaz por constructor.
  4. Proporciona la instancia concreta desde la raíz de la aplicación o mediante un contenedor DI.

Esto reduce el acoplamiento oculto sin interrumpir el funcionamiento de la aplicación.

Preguntas y respuestas rápidas

¿Cuándo usar un Singleton?

Usa un Singleton con moderación: cuando el recurso debe ser verdaderamente único y sin estado crítico, como un logger central o un adaptador de hardware. Prefiere DI para mejorar testabilidad.

¿Cómo afectan los Singletons a las pruebas unitarias?

Introducen estado global que puede filtrarse entre pruebas y dificultar el mocking. La solución habitual es extraer una interfaz y permitir la inyección de dobles en los tests.

¿Cuál es la forma más segura de eliminar Singletons de una app legacy?

Mapea usos, crea una interfaz, refactoriza consumidores para aceptar la interfaz y pasa la instancia desde la raíz o mediante un contenedor DI.

Preguntas frecuentes (Q&A)

¿El Singleton mejora el rendimiento? A veces, porque evita crear recursos costosos varias veces; sin embargo, el coste de una mala arquitectura puede superar la ganancia. Revisa el caso de uso antes de decidir.

¿Puede un Singleton implementarse de forma segura en entornos concurrentes? Sí, pero hay que evitar estado mutable compartido o aplicar sincronización. En JavaScript/Node la concurrencia es cooperativa, pero en entornos multihilo o con workers conviene delegar estado a componentes thread-safe.

¿Qué ventajas ofrece un contenedor DI frente a un Singleton clásico? Un contenedor DI hace explícito el ciclo de vida de las dependencias (transitorio, por alcance, singleton a nivel de contenedor), mejora la testabilidad y reduce el acoplamiento oculto.


1.
Martin Fowler, “Singleton,” Bliki, https://martinfowler.com/bliki/Singleton.html
2.
3.
Mark Seemann, “Singletons Are Pathological Liars,” https://blog.ploeh.dk/2010/07/28/SingletonsArePathologicalLiar/
4.
NestJS Documentation, “Dependency Injection,” https://docs.nestjs.com/fundamentals/injection
5.
InversifyJS Documentation, https://inversify.io/
6.
Stack Overflow, “Developer Survey 2023,” https://insights.stackoverflow.com/survey/2023
7.
GitHub, “State of the Octoverse 2023,” https://octoverse.github.com/
← Back to blog
🙋🏻‍♂️

La IA escribe código.
Tú lo haces durar.

En la era de la aceleración de la IA, el código limpio no es solo una buena práctica — es la diferencia entre sistemas que escalan y bases de código que colapsan bajo su propio peso.