Typescript-generiska typer: Hänvisa till klass istället för instans
Jag har en situation där jag vill använda en generisk typ i en uppsättning gränssnitt. Mitt publika gränssnitt tar emot den generiska typen och skickar ner den till de icke-exporterade privata gränssnitten. I mitt publika gränssnitt vill jag använda den generiska typen T, men jag vill inte att den refererar till en instans av T. Jag vill att den ska ange att det är T, klassen, som kan skapa instanser av T.
När jag försöker detta får jag ett fel:
interface Car<T> {
unit: T;
progress: number;
}
export interface CarFactory<T> {
cars: Car<T>[];
// Type error: 'T' only refers to a type, but is being used as a value here. TS2693
blueprint: typeof T;
}
Att använda en generatorfunktion fungerar. Men då måste jag skicka ner det och exponera mer av internals i min kod, vilket jag vill undvika.
interface CarFactory<T> {
blueprint: (args: any) => T;
}
Jag kan inte använda T direkt, eftersom det får kompilatorn att tro att det borde ta emot en instans av T, inte klassen. Detta utlöser ett TS2740-fel. Att använda T=CarModel och T['constructor'] som blueprint-typ fungerar, men bara om jag patchar min klass på detta sätt:
class CarModel {
public ['constructor']: typeof CarModel;
}
Så frågan är: Hur kan jag använda generics på det här sättet? Använda båda instanserna av T och den faktiska T? Är en generatorfunktion eller ['constructor'] med en patchad T mina enda alternativ? Skulle jag behöva skicka ner en annan generisk typ U som är något i stil med U=typeof T?
Svar #1
Menar du något liknande detta?
Givet en klass Car är typen av klassen själv (symbolen Car som refererar till klassens konstruktörsfunktion) typeof Car. I CarFactory-gränssnittet har vi ingen specifik klass, så vi kan använda typen av konstruktörsfunktionen new (...args: any) => T för blueprint:
export interface CarFactory<T> {
cars: Car<T>[];
// use a constructor function type here
blueprint: new (...args: any) => T;
}
Testa det:
class CombiUnit {
// your implementation of a car unit/model T goes here
}
type CombiUnitFactory = CarFactory<CombiUnit>;
// new (...args: any) => CombiUnit
type CombiUnitFactoryCtorFunction = CombiUnitFactory["blueprint"];
// some concrete factory
declare const t1: CombiUnitFactory;
const result = new t1.blueprint(); // const result: CombiUnit
Lekplats
Svar #2
Operatorn typeof returnerar effektivt ett värde. Det är tillräckligt att skriva
interface Car<T> {
blueprint: T;
}
Förutom det, tror jag att en fabriksklass bör vara statisk, och därför inte kräver ett gränssnitt. Du kan istället göra en statisk bilfabriksklass så här:
export class AutomobileFactory {
static createCar<T>(): Car<T> {
// create a concrete car implementation of the
// generic type T instead of throwing an error
throw new Error("Not implemented");
}
}
