TypeScript中带protected构造函数的泛型getInstance调用报错解决方案
解决TypeScript中带protected构造函数的泛型单例实现问题
问题根源
原代码的错误在于getInstance方法的this参数类型new () => T要求构造函数是**公共(public)**的,但子类的构造函数是受保护(protected)的,TypeScript不允许将受保护构造函数类型赋值给公共构造函数类型。此外,原代码所有子类共享父类的instance属性,导致多个子类无法拥有各自的单例实例。
解决方案
我们可以通过以下调整实现需求:
- 使用
Map存储每个子类的单例实例,确保每个子类拥有独立的单例 - 修改
this参数类型为父类的类型typeof Singleton,兼容受保护构造函数的子类 - 在父类静态方法中利用类层级访问权限创建子类实例
修正后的代码
class Singleton { // 用Map存储每个子类的单例实例,键为类构造函数,值为实例 private static instances = new Map<typeof Singleton, Singleton>(); protected constructor() {} public static getInstance<T extends Singleton>(this: typeof Singleton): T { if (!Singleton.instances.has(this)) { // 利用父类对protected构造函数的访问权限创建实例 Singleton.instances.set(this, new this()); } // 返回对应子类的实例,类型断言为T是安全的 return Singleton.instances.get(this) as T; } } class Child extends Singleton { someMethod(): void { console.log("Child method called"); } } // 现在调用不再报错 const childInstance = Child.getInstance(); childInstance.someMethod(); // 输出: Child method called // 测试子类的单例特性 console.log(childInstance === Child.getInstance()); // true // 新增子类验证独立单例 class GrandChild extends Child { anotherMethod(): void { console.log("GrandChild method called"); } } const grandChildInstance = GrandChild.getInstance(); grandChildInstance.anotherMethod(); // 输出: GrandChild method called console.log(grandChildInstance === GrandChild.getInstance()); // true console.log(childInstance !== grandChildInstance); // true
关键说明
- Map存储实例:避免了所有子类共享父类实例的问题,每个子类通过自身构造函数作为键获取独立实例
- this参数类型调整:
typeof Singleton兼容所有子类的类型(即使构造函数是protected),解决了原有的类型不匹配错误 - protected构造函数访问:父类的静态方法属于类层级,有权访问子类的protected构造函数,因此
new this()是合法的
内容的提问来源于stack exchange,提问作者Nolesh
相关产品推荐
相关产品推荐

