Ein Konstruktor in TypeScript ist die Methode constructor einer Klasse mit typisierten Parametern. Er hat keine Annotation für den Rückgabetyp: Er erzeugt immer eine Instanz der Klasse.
constructor(): Product zu schreiben ist der Fehler TS1093 (Type annotation cannot appear on a constructor declaration). Alles andere daran, wie new funktioniert, ist reines JavaScript.
Parameter Properties
Das Muster oben (ein Feld deklarieren, einen Parameter nehmen, ihn hinüberkopieren) ist so verbreitet, dass TypeScript eine Kurzform dafür hat. Setze public, private, protected oder readonly vor einen Konstruktorparameter, und er wird zu einem Feld:
Das ist eines der wenigen TypeScript-Features, die Code erzeugen. Der Compiler schreibt die Zuweisungen für dich:
class Product {
name;
price;
sku;
constructor(name, price, sku) {
this.name = name;
this.price = price;
this.sku = sku;
}
// ...
}
Weil das nicht nur entfernbare Typen sind, laufen Parameter Properties nicht mit dem eingebauten Type Stripping von Node (node file.ts scheitert mit ERR_UNSUPPORTED_TYPESCRIPT_SYNTAX), und die Compileroption erasableSyntaxOnly meldet sie als Fehler TS1294. Projekte, die .ts-Dateien direkt ausführen, schreiben die Felder von Hand aus.
Optionale Parameter und Standardwerte
Konstruktorparameter folgen denselben Regeln wie Funktionsparameter: ? macht einen optional, ein Standardwert macht ihn optional und füllt ihn aus, und Pflichtparameter stehen vorn.
Auch Parameter Properties können Standardwerte haben. Der Typ von color in der Klasse ist string | undefined, Code, der es verwendet, muss also zuerst auf undefined prüfen.
Bei Konstruktoren mit vielen Optionen liest sich ein Options-Objekt besser als eine lange Liste von Positionen: constructor(opts: { prefix: string; level?: "info" | "debug" }).
Reihenfolge der Feldinitialisierung
Feldinitialisierer laufen vor dem Rumpf des Konstruktors, und Parameter Properties werden am Anfang des Konstruktorrumpfs zugewiesen. Ein Feldinitialisierer kann also keine Parameter Property lesen. TypeScript findet das:
index.ts(3,18): error TS2729: Property 'size' is used before its initialization.
index.ts(3,30): error TS2729: Property 'size' is used before its initialization.
Zur Laufzeit würde der Initialisierer undefined sehen und NaN erzeugen. Berechne den Wert stattdessen im Konstruktorrumpf:
class Grid {
cells: number;
constructor(public size: number) {
this.cells = size * size;
}
}
console.log(new Grid(3).cells); // 9
Konstruktor-Überladungen
Eine Klasse hat genau eine Konstruktorimplementierung, aber du kannst mehrere Overload Signatures darüber aufführen. Aufrufer sehen nur die Überladungen; die Implementierungssignatur muss mit allen kompatibel sein.
Überladungen werden ab zwei oder drei Formen schwer lesbar. Statische Factory-Methoden mit sprechenden Namen (Color.fromHex("#ff8800"), Color.fromRgb(10, 20, 30)) sind oft klarer und brauchen kein Narrowing innerhalb eines Rumpfs.
super in einer Unterklasse aufrufen
Eine abgeleitete Klasse, die einen eigenen Konstruktor deklariert, muss super(...) mit den Argumenten der Elternklasse aufrufen, und zwar bevor sie this berührt. Den Aufruf zu vergessen ist der Fehler TS2377 (Constructors for derived classes must contain a 'super' call), und this zuerst zu verwenden ist TS17009 ('super' must be called before accessing 'this' in the constructor of a derived class). Beide spiegeln Laufzeitregeln von JavaScript wider.
class Animal {
constructor(public name: string) {}
}
class Dog extends Animal {
tricks: string[];
constructor(name: string, ...tricks: string[]) {
super(name); // must come first
this.tricks = tricks;
}
}
const rex = new Dog("Rex", "sit", "roll");
console.log(rex); // Dog { name: 'Rex', tricks: [ 'sit', 'roll' ] }
Eine Unterklasse ohne eigenen Konstruktor erbt den der Elternklasse, mit denselben Parametertypen. Mehr zu Unterklassen unter Vererbung.
Private Konstruktoren und asynchrone Initialisierung
Ein private Konstruktor kann nur innerhalb der Klasse aufgerufen werden. Außerhalb ist new der Fehler TS2673. So zwingst du Aufrufer über eine Factory, für ein Singleton oder für Initialisierung, die asynchron sein muss (Konstruktoren können nicht async sein).
private ist hier nur eine Regel beim Kompilieren. Das erzeugte JavaScript hat einen gewöhnlichen Konstruktor, reiner JavaScript-Code könnte also weiterhin new Connection(...) aufrufen.
Konstruktortypen
Um eine Klasse als Wert anzunehmen, typisierst du den Parameter mit einer Construct Signature: new (...args) => Instance. typeof MyClass funktioniert auch, bindet den Parameter aber an die Konstruktorsignatur und die statischen Member genau dieser einen Klasse.
ConstructorParameters und InstanceType holen das Parametertupel und den Instanztyp aus einem Konstruktortyp; sie werden zusammen mit den anderen Hilfstypen für Funktionen unter ReturnType und Parameters behandelt.
Häufig gestellte Fragen
Was sind Parameter Properties in TypeScript?
Ein Konstruktorparameter mit einem Modifikator (public, private, protected oder readonly) deklariert ein Feld und weist es in einem Schritt zu. constructor(private name: string) {} ist die Kurzform dafür, private name: string zu deklarieren und this.name = name zu schreiben. Der Compiler erzeugt diese Zuweisung in der Ausgabe.
Kann eine TypeScript-Klasse mehrere Konstruktoren haben?
Nicht mehrere Implementierungen. Eine Klasse hat einen Konstruktor, aber du kannst mehrere Overload Signatures darüber schreiben, sodass Aufrufer verschiedene Parameterlisten sehen. Statische Factory-Methoden wie Color.fromHex() und Color.fromRgb() sind oft klarer als Überladungen.
Kann ein Konstruktor in TypeScript async sein?
Nein. Ein Konstruktor gibt immer die neue Instanz zurück, nie ein Promise, und async constructor() wird mit dem Fehler TS1089 abgelehnt ('async' modifier cannot appear on a constructor declaration). Nimm einen privaten Konstruktor plus eine statische Methode async create(), die die asynchrone Arbeit erledigt und dann new aufruft.
Wie typisiere ich einen Klassenkonstruktor als Parameter?
Mit einer Construct Signature: new (name: string) => User oder typeof User für genau diese Klasse. Eine generische Factory sieht so aus: function make<T>(ctor: new () => T): T { return new ctor(); }. ConstructorParameters<typeof User> liefert die Parameterliste als Tupel.
Warum bekomme ich "'super' must be called before accessing 'this'"?
Das ist der Fehler TS17009. In einer Klasse, die mit extends eine andere erweitert, erzeugt der Elternkonstruktor das Objekt, this existiert also erst, wenn super(...) gelaufen ist. Verschiebe den Aufruf von super über jede Zeile, die this berührt.