0

export class Thing{
    name: string = "dunno"
}

export interface IMakeThing<T extends new (...args: any) => any>{
    value: T
}

const owner = new Thing();

const test:  IMakeThing<Thing> = {
    value: new Thing
};

It complains when using the Thing as a generic param on IMakeThing. But I am not sure why as the generic constraint asks for a constructor function.

Playground link here https://www.typescriptlang.org/play?noImplicitAny=false&strictFunctionTypes=false&strictBindCallApply=false&target=1&module=1&ts=4.1.3&ssl=14&ssc=3&pln=1&pc=1#code/LAKApgHgDg9gTgFwAQGMA2BDAzlpAVACwEsA7AcwG9QkakSMBbMALiSwTlLKQF4kAiACYBXEiRj9QAX1ChIsRElIIwcAGYYUYJAEkAshgDWYQlwA8eJJBUlBuEmADuSABQA6DxjhksrDCQBPAEpeAD4kfwDQqhBaJAA3DDRhFnxpWRAUGBJ2JBhHBzheOid8YnIXIIBuDKyc5BV2Vl0DY1NyC3KycL4YuMTk1IdndrJpKqA

CompareTheMooCat
  • 1,047
  • 2
  • 10
  • 14
  • 1
    Because the type named `Thing` refers to the instance type, while the value named `Thing` refers to the constructor, whose type is not `Thing` but `typeof Thing` which is more like `new() => Thing`. – jcalz Feb 13 '21 at 14:47
  • See https://stackoverflow.com/questions/58399613/what-is-exactly-the-static-side-and-the-instance-side-in-typescript – jcalz Feb 13 '21 at 14:50

1 Answers1

0

For convenience, class types are the type of the class objects, and the type of the actual class (the constructor) is typeof Class. So, in this case, you'd have to use IMakeThing<typeof Thing>, not IMakeThing<Thing>. You have another issue where value will then have to be the actual class object, so you could make a ConstructedType utility type that extracts the type constructed and uses that for the type of value:

export class Thing{
    name: string = "dunno"
}

type ConstructedType<T extends new (...args: any) => any>
    = T extends new (...args: any) => infer R ? R : never;

export interface IMakeThing<T extends new (...args: any) => any>{
    value: ConstructedType<T>
}

const owner = new Thing();

const test:  IMakeThing<typeof Thing> = {
    value: new Thing
};

Or just use the much prettier approach of just using the object type from the beginning since you don't need the constructor type:

export class Thing{
    name: string = "dunno"
}

export interface IMakeThing<T>{
    value: T
}

const owner = new Thing();

const test:  IMakeThing<Thing> = {
    value: new Thing
};

If you actually mean to put the constructor function under value, then just pass Thing, not new Thing, since new Thing expands to new Thing():

export class Thing{
    name: string = "dunno"
}

export interface IMakeThing<T extends new (...args: any) => any>{
    value: T
}

const owner = new Thing();

const test:  IMakeThing<typeof Thing> = {
    value: Thing
};
Aplet123
  • 33,825
  • 1
  • 29
  • 55