如何为TypeScript单例类的实例创建传递配置选项?
Great question! The basic singleton implementation you shared works for no-args initialization, but we can tweak it to accept configuration options (like setting production mode) while preserving the "single instance" guarantee. Here are a couple of robust approaches:
Approach 1: Pass Config on First getInstance() Call
This method ensures the singleton is initialized exactly once with the provided config, and ignores any subsequent config attempts. It’s ideal if you know the config upfront when first accessing the singleton.
First, define a type for your configuration to enforce type safety:
interface SingletonConfig { isProduction: boolean; // Add other config properties as needed, e.g., apiUrl: string }
Then update the singleton class:
class MySingleton { private static _instance: MySingleton; // Make config readonly to prevent external modifications public readonly config: SingletonConfig; // Private constructor forces use of getInstance() private constructor(config: SingletonConfig) { // Create a copy of the config to avoid external reference mutations this.config = { ...config }; } public static getInstance(config?: SingletonConfig): MySingleton { // Return existing instance if it already exists if (MySingleton._instance) { if (config) { console.warn("Singleton instance already exists — provided config will be ignored."); } return MySingleton._instance; } // Enforce config is provided on first call if (!config) { throw new Error("Config must be provided when initializing the singleton for the first time."); } MySingleton._instance = new MySingleton(config); return MySingleton._instance; } }
Usage Example:
// First call: initializes with config const prodInstance = MySingleton.getInstance({ isProduction: true }); console.log(prodInstance.config.isProduction); // true // Subsequent calls: return existing instance, ignore new config const devInstance = MySingleton.getInstance({ isProduction: false }); console.log(devInstance.config.isProduction); // still true
Approach 2: Deferred Configuration (Lazy Setup)
If you can’t provide the config upfront (e.g., you load it asynchronously), this approach lets you fetch the singleton first, then configure it exactly once before using it.
interface SingletonConfig { isProduction: boolean; } class MySingleton { private static _instance: MySingleton; private _config?: SingletonConfig; private _isConfigured = false; private constructor() {} public static getInstance(): MySingleton { if (!MySingleton._instance) { MySingleton._instance = new MySingleton(); } return MySingleton._instance; } // Configure the singleton — can only be called once public configure(config: SingletonConfig): void { if (this._isConfigured) { throw new Error("Singleton has already been configured."); } this._config = { ...config }; this._isConfigured = true; } // Example method that relies on config public execute(): void { if (!this._config) { throw new Error("Singleton must be configured before use."); } console.log(`Running in ${this._config.isProduction ? "production" : "development"} mode`); } }
Usage Example:
// Get the instance first const singleton = MySingleton.getInstance(); // Load config asynchronously (e.g., from an API) fetch("/app-config") .then(res => res.json()) .then(config => { singleton.configure(config); singleton.execute(); // Logs "Running in production mode" if config.isProduction is true });
Both approaches maintain the singleton pattern’s core rule (only one instance exists) while adding the flexibility to pass configuration. Pick the one that fits your use case best!
内容的提问来源于stack exchange,提问作者Kartik Bhandari

