Nel mondo dello sviluppo, il Singleton assicura un’unica istanza condivisa per risorse critiche. Questa guida mostra come implementarlo in TypeScript, quando evitarlo e come migrare verso DI per migliorare testabilità e manutenibilità.
January 19, 2026 (5mo ago) — last updated May 30, 2026 (1mo ago)
Singleton in TypeScript: Guida completa
Scopri il pattern Singleton in TypeScript: esempi type-safe, pro e contro, alternative come iniezione delle dipendenze e come rifattorizzare code legacy per test più affidabili.
← Back to blog
Singleton in TypeScript: Guida completa

Nel mondo dello sviluppo software, alcuni pattern sono potenti ma vanno usati con criterio. Il Singleton garantisce che una classe abbia una sola istanza e fornisce un punto di accesso globale alla stessa, utile per risorse uniche come il logging o la configurazione centrale1.
Questa guida pratica mostra quando usare il Singleton, come implementarlo in TypeScript in modo type-safe, quali problemi introduce e quali alternative preferire, come l’iniezione delle dipendenze. Troverai esempi concreti, consigli per il refactor in codebase legacy e riferimenti autorevoli per approfondire.
Cos’è il pattern Singleton e quando usarlo
Immagina uno Scriba Reale: unica fonte autorizzata per i registri ufficiali. Nel software, il Singleton funziona allo stesso modo: limita l’oggetto a un’unica istanza che diventa la fonte di verità. Questo è utile quando duplicare la risorsa causa conflitti o spreco di risorse.
Scopo principale e analogia
Il Singleton centralizza il controllo. Proprio come lo Scriba Reale, offre un punto di accesso unico che evita versioni isolate e incoerenti dei dati. Un pool di connessioni al database o un logger centralizzato sono esempi tipici.
Caratteristiche chiave
- Singola istanza tramite costruttore privato o controllato.
- Punto di accesso globale, spesso tramite metodo statico come getInstance().
- Inizializzazione lazy per ritardare il costo fino al primo uso.
- Può gestire stato condiviso come impostazioni o sessioni.
Casi d’uso pratici
Il Singleton è appropriato quando una risorsa è intrinsecamente unica:
- Servizi di logging centralizzati.
- Gestione della configurazione globale.
- Interfacce hardware che non possono avere più istanze concorrenti.
Vantaggi e limiti

Vantaggi
- Punto di accesso semplice e uniforme.
- Riduzione della duplicazione e risparmio di risorse.
- Inizializzazione lazy migliora i tempi di avvio.
Svantaggi
- Accoppiamento forte: il codice può nascondere dipendenze.
- Stato globale mutabile che rende debugging e test più difficili.3
- Problemi di concorrenza se non gestito correttamente.
I Singleton possono complicare i test unitari perché introducono stato persistente tra i test. Per questo molte squadre preferiscono l’iniezione delle dipendenze, che rende le dipendenze esplicite e più facili da sostituire nei test3.
Implementare un Singleton type-safe in TypeScript
Passiamo alla pratica con un esempio: un ConfigManager che carica impostazioni e le fornisce all’applicazione. Questo esempio usa un costruttore privato e un metodo statico per l’accesso all’istanza2.
class ConfigManager {
private static instance: ConfigManager;
private settings: Map<string, any> = new Map();
private constructor() {
console.log("Initializing ConfigManager instance...");
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(); // Errore: costruttore privato
Uso in un servizio
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}...`);
// Logica di fetch reale qui.
}
}
// --- Application Entry Point ---
console.log("Application starting...");
const service1 = new ApiService();
service1.fetchData();
const service2 = new ApiService();
console.log("Application finished.");
Questo dimostra che ConfigManager viene inizializzato una sola volta e condiviso.
Perché i Singleton hanno una cattiva reputazione
Il problema principale è lo stato globale. I Singleton nascondono dipendenze nel codice, rendendo difficile capire cosa usa cosa e rendendo i test fragili. Condizioni di race possono emergere in scenari concorrenti senza sincronizzazione adeguata.
Test e concorrenza
Un esempio concreto è un SessionCounter condiviso: due richieste simultanee possono leggere e scrivere lo stesso stato creando bug non deterministici. I test possono iniziare a dipendere dall’ordine di esecuzione, rendendo le suite instabili3.
Alternative moderne al Singleton
L’approccio raccomandato è introdurre l’iniezione delle dipendenze. Invece di chiamare getInstance() implicitamente, i componenti ricevono le dipendenze tramite il costruttore o tramite un container IoC.
Esempio: DI invece di Singleton
interface IConfigManager {
get(key: string): any;
}
class ApiService {
private apiUrl: string;
constructor(config: IConfigManager) {
this.apiUrl = config.get("API_URL");
}
}
Con questa versione, ApiService dipende da un contratto e nei test basta passare un fake o un mock.
Container IoC e lifecycle
Framework come NestJS o librerie come InversifyJS gestiscono il ciclo di vita degli oggetti, permettendo scope transitori, scoped o singleton gestiti dal container, ottenendo i benefici di istanze condivise senza l’accoppiamento nascosto di un Singleton programmato manualmente45.
Rifattorizzare Singleton in codebase legacy
Procedi in modo incrementale:
- Individua gli utilizzi di MySingleton.getInstance().
- Definisci un’interfaccia che descriva le responsabilità pubbliche.
- Rifattorizza un consumer alla volta per accettare l’interfaccia tramite costruttore.
- Nella radice di composizione crea e inietta l’istanza concreta o lascia che il container DI la gestisca.
Questo riduce l’accoppiamento nascosto senza interrompere l’applicazione.
Risposte rapide — FAQ essenziale
D: I Singleton sono sempre una cattiva idea? A: No. Possono essere adatti per servizi veramente unici e privi di stato, come un logger centrale. Tuttavia, preferisci DI per una migliore testabilità e modularità.
D: Come testare codice che usa un Singleton ora? A: Introduci un’interfaccia, rifattorizza i consumer per accettarla e inietta un test double in fase di test. Procedi per piccoli passi per evitare regressioni.
D: Classe statica o Singleton: differenze? A: Una classe statica ha solo membri statici e non può essere passata come oggetto. Un Singleton è una vera istanza che può implementare interfacce e venire iniettata.
Q&A rapide (3 domande frequenti)
Q: Quando preferire il Singleton? A: Solo per risorse intrinsecamente uniche e senza stato; altrimenti scegli DI.
Q: Come riduco l’impatto dei Singleton sui test? A: Definisci interfacce e inietta dipendenze, usa test double e reset degli stati condivisi tra test.
Q: Il container DI sostituisce sempre il Singleton? A: Sostituisce il pattern programmativo di Singleton offrendo istanze condivise gestite centralmente, con controllo sul ciclo di vita.
Prossimi passi per il team
Avvia una discussione su dove sia realmente richiesta un’istanza unica. Prototipa la DI in un modulo isolato, adotta standard di codice chiari e misura il debito tecnico. Pair programming e code review aiutano a rendere i refactor più sicuri.
Ricorda: nessun pattern è una bacchetta magica. Usa i Singleton con giudizio, accompagnandoli con interfacce pulite e regole chiare di ownership.
L'AI scrive codice.Tu lo fai durare.
Nell'era dell'accelerazione AI, il codice pulito non è solo una buona pratica — è la differenza tra sistemi che si scalano e codebase che collassano sotto il loro stesso peso.