Home > Software design >  How to write a static method that returns a subclass instance
How to write a static method that returns a subclass instance

Time:10-04

I'm trying to create an abstract class Enum, which has a bunch of static methods that return instances of the class they were called on. My problem is that I can't figure out how to type these methods correctly.

Essentially, I want TypeScript to understand that EnumSubclass.getFirstInstance() returns a EnumSubclass instance, and not an Enum instance.

Here is my best attempt so far:

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> {
        // No warning here, TypeScript understands that this.getInstances exists
        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 not assignable to method's 'this' of type 'typeof Enum'.
//   Types of construct signatures are incompatible.
//     Type 'new (name: string, bar: boolean) => Demo' is not assignable to type 'abstract new (name: string) => Enum'.ts(2684)
const x: Demo = Demo.getFirstInstance()

Apparently TypeScript has a problem with the fact that the constructor of Demo has a different signature than the constructor of Enum, even though Enum never even calls the constructor - it returns instances that already exist.

How can I convince TypeScript that this is not an error?

P.S.: Ideally, I would like to:

  1. Keep the constructors protected and not make them public
  2. Have correct typing inside the implementation of Enum (for example, there shouldn't be a warning at this.getInstances())

CodePudding user response:

That's long awaited feature that has yet to be implemented. #5863

So not possible at the moment.

  • Related