3

What I want to achieve is to have a function with a generic type T and to be able to create objects of said type.

I figured I had to use construct signatures and I thought that I could use a generic constraint to define that my class needs a constructor. Yet, this did not work, as shown in Example 1. Instead, if I define my function's parameter to have the construct signature as type instead of using a generic constraint, it suddenly work.

I still cannot make out why Example 1 does not work. I found this StackOverflow Q&A which talks about things such as static side and instance side of a class, which are in turn in more detail explained here. Nevertheless, I cannot make out how exactly this relates to my problem.

I thought that it might be due to the fact that type may describe an instance of T (and not the class itself), but then a colleague came up with Example 3, which (for whatever reason) works!

I hope somebody can shed some light on this for me.

Example 1 (not working): The following does not work (create(Person) gives the error Argument of type 'typeof Person' is not assignable to parameter of type 'new (...args: any[]) => typeof Person'.:

class Person {
  public name: string
  constructor(name: string = '') {
    this.name = name
  }
}

function create<T extends { new(...args:any[]) : T }> (type : T) : T {
    return new type()
}

console.log(create(Person))

Example 2 (working):

class Person {
  public name: string
  constructor(name: string = '') {
    this.name = name
  }
}

function create<T> (type : { new(...args:any[]) : T } ) : T { // this line has been changed
    return new type()
}

console.log(create(Person))

Example 3 (working):

class Person {
  public name: string
  constructor(name: string = '') {
    this.name = name
  }
}

function create<T extends { new(...args:any[]) : any }> (type : T) : InstanceType<T> { // this line has been changed
    return new type()
}

console.log(create(Person))
Markus Weninger
  • 11,931
  • 7
  • 64
  • 137
  • On the face of it, `T extends { new(...args: any[]): T` means that if you have a value `type` of type `T`, you could write `new type()` and it would result in a value of type `T`. Meaning you could write `new (new type())()` or `new (new (new (new type())())())()` etc. Is that what the `Person` constructor does? No, it just makes `Person` instances and those are not themselves constructors. That's why example 1 fails, and neither of your other examples have that issue. Does that explain things sufficiently (and I'd write up a more detailed answer) or is there still something missing? – jcalz May 10 '23 at 14:17

0 Answers0