I am currently working on creating an abstract class called Enum
, which consists of static methods that return instances of the class they are invoked upon. However, I am encountering difficulties in properly typing these methods.
My goal is to help TypeScript understand that EnumSubclass.getFirstInstance()
should produce an instance of EnumSubclass
, not just any generic Enum
instance.
This is my current approach:
type InstanceOf<T> = T extends { prototype: infer R } ? R : never
const instances = {}
abstract class Enum {
protected constructor(
public readonly name: string,
){
instances[name] = this
}
public static getInstances<T extends typeof Enum>(this: T): InstanceOf<T>[] {
return Object.values(instances)
}
public static getFirstInstance<T extends typeof Enum>(this: T): InstanceOf<T> {
// TypeScript acknowledges the presence of this.getInstances and there's no warning
return this.getInstances()[0]
}
}
class Demo extends Enum {
public static readonly FOO = new Demo('foo', true)
protected constructor(
name: string,
public readonly bar: boolean,
){
super(name)
}
}
// The 'this' context of type 'typeof Demo' is incompatible with method's 'this' of type 'typeof Enum'.
// Construct signatures differ.
// Type 'new (name: string, bar: boolean) => Demo' cannot be assigned to type 'abstract new (name: string) => Enum'.ts(2684)
const x: Demo = Demo.getFirstInstance()
It seems like TypeScript is struggling with the difference between the constructor signature of Demo
and that of Enum
. It's worth noting that Enum
doesn't actually call the constructor - it merely returns existing instances.
How can I assure TypeScript that this discrepancy is not an issue?
P.S.: Ideally, I aim to:
- Maintain the constructors as
protected
without changing them topublic
- Ensure correct typing within the
Enum
implementation (e.g., no warnings atthis.getInstances()
)