0

How to type the constructWhenReady method in such a way that the return type is simply just Child, without changing the 'calling' code or the Child class. Basically I am just searching for a solution to have an async constructor, but the proposed solutions fall apart when the Parent class is generic.

class Parent<PropType extends any> {
    static isReadyForConstruction = new Promise(r => setTimeout(r, 1000));

    static async constructWhenReady<T extends typeof Parent>(this: T): Promise<InstanceType<T>> {
        await this.isReadyForConstruction;
        return new this() as InstanceType<T>;
    }

    get something(): PropType {
       // ...
    }
}

class Child extends Parent<{ a: 'b' }> { }

// Error: '{ a: "b"; }' is assignable to the constraint of type 
// 'PropType', but 'PropType' could be instantiated with a different 
// subtype of constraint 'unknown'.
const x = await Child.constructWhenReady(); 


Rationale: The Child classes and the code consuming the Child classes will be written by less experienced developers and need to be as clean as possible (it's test automation code). The simplest solution would be do to just require everyone to write their code as await new Child().init(), which is fine, but I would far rather have an error message that pops up whilst they are typing their code rather than during runtime.

1 Answer 1

2

The constructWhenReady method doesn't care which type the generic argument PropType is instantiated with. It should explicitly state so:

class Parent<PropType> {
    static isReadyForConstruction = new Promise(r => setTimeout(r, 1000));

    static async constructWhenReady<T extends typeof Parent<unknown>>(this: T): Promise<InstanceType<T>> {
    //                                                     ^^^^^^^^^
        await this.isReadyForConstruction;
        return new this() as InstanceType<T>;
    }

    get something(): PropType {
       // ...
    }
}

Notice that you can even get rid of the as InstanceType<T> cast when you write

    static async constructWhenReady<T extends Parent<unknown>>(this: {new(): T; isReadyForConstruction: Promise<unknown>}): Promise<T> {
        await this.isReadyForConstruction;
        return new this();
    }
Sign up to request clarification or add additional context in comments.

1 Comment

I was about to downvote, as it threw a bunch of syntax errors in the TS Playground where I was trying to figure this out... before luckily in time realizing that I was running in the playground on 4.6.4 for unrelated reasons 🤦 . Thank you!!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.