Hur kan jag specificera att en typ är en instans av en given klastyp i TypeScript?


Jag vill skapa en Constructor-typ som tar en klass som en typparameter och returnerar signaturen för dess konstruktor.

Detta skulle vara användbart för att definiera en statisk egenskap make-metod på en klass som kan skapa en instans av den klassen istället för att skapa den med ett new ClassInstance(...)-uttryck.

Det här är vad jag har hittills:

type ClassConstructorArgs<T> = T extends { new (...args: infer A): any }
  ? A
  : never;

type Constructor<T> = (...args: ClassConstructorArgs<T>) => T;

class Person {
  constructor(public name: string, public age: number) {}

  static make: Constructor<typeof Person> = (...args) => new Person(...args);
  //                                                     ^^^^^^^^^^^^^^^^^^^
  // Type 'Person' is missing the following properties from type 'typeof Person': prototype, make
}

Problemet är att den statiska make har ett fel: Typen 'Person' saknar följande egenskaper från typen 'typeof Person': prototype, make

Jag förstår att detta beror på att min Constructor-typ är fel, konstruktörer av klassen T returnerar inte klassen T själv utan en instans av T.

Men sedan vet jag inte hur jag kan uttrycka att Constructor returnerar en instans av T snarare än klassen T själv. Är det ens möjligt i TypeScript?

Svar #1

Typescript har inbyggda verktyg både för ConstructorParameters och InstanceType:

type Constructor<T extends new (...args: any) => any> = 
  (...args: ConstructorParameters<T>) => InstanceType<T>;

class Person {
  constructor(public name: string, public age: number) {}

  static make: Constructor<typeof Person> = (...args) => new Person(...args);
}

const p = Person.make('some name', 1); // p is of type Person

Lekplats

Om du undrar hur dessa verktyg definieras och vad som är fel med ditt försök, här är det:

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

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

Källa

Relaterade artiklar