thephpdev
thephpdev

Reputation: 1117

TypeScript Generics: Function that returns constructor of instance specified as parameter

I'm learning TypeScript. I've been getting my head around generics, and I've encountered a slight issue that I'm struggling to find answers for.

I have Googled lots and lots of search queries, but because of the ambiguity of the search terms, and lack of knowledge of technical names for certain language constructs, I'm getting lots of noise.

I read three relevant pages of documentation on TypeScript's website, and could not find the information I need. Perhaps I overlooked something, and if that is the case, a link to a good explanation would be appreciated.

I want to write a function that returns the constructor of an instance specified as a parameter that retains typing, since this.constructor in TypeScript is not of the type of the class.

To be very clear, I know I can do this:

const aa2 = (a.constructor as typeof A).make();

This is a stripped down case:

class A {
    prop1: string;
    prop2: number;

    constructor(prop1: string, prop2: number) {
        this.prop1 = prop1;
        this.prop2 = prop2;
    }

    method() {

    }

    static make() {
        return new this('test', 42);
    }
}

function constructor<T>(instance: T): { new(...args): T } {
    return instance.constructor as { new(...args): T };
}

const a = A.make();

// Property 'make' does not exist on type 'new(...args: any[]) => A'
const aa = constructor(a).make();


// This works, but is not pretty
const aa2 = (a.constructor as typeof A).make();

It needs to work on any class, with constructor parameters or without.

I can do the inverse:

export function make<T>(type: { new(...args): T }, ...args): T {
    return new type(...args);
}

make(A, 'test', 42).method();

It seems there is some critical element to this I'm not getting. If you can spot precisely where I'm going wrong in my thinking, I welcome a good explanation of my misunderstanding.

Upvotes: 1

Views: 48

Answers (1)

Titian Cernicova-Dragomir
Titian Cernicova-Dragomir

Reputation: 249636

There is no way to move from an instance type to the class type. This would only be possible if constructor was typed as the class type (which it is not) There is an open ticket to make this happen but it has been open and in discussion for a long time.

If you control the client classes you can add a member to the class to make the method work correctly:

class TraceableClass<TThis> {
  private typedConstructor!: TThis
}
class A<TThisClas = typeof A> extends TraceableClass<TThisClas>{
    prop1: string;
    prop2: number;

    constructor(prop1: string, prop2: number) {
        super();
        this.prop1 = prop1;
        this.prop2 = prop2;
    }

    method() {

    }

    static make() {
        return new this('test', 42);
    }
}

function constructor<T>(instance: TraceableClass<T>): T {
    return instance.constructor as  any as T;
}

const a = A.make();

// Ok now
const aa = constructor(a).make();


Play

Upvotes: 3

Related Questions