In JavaScript wurde der Spread-Operator (...) eingeführt, um das Kopieren und Erweitern von Objekten zu erleichtern; bei Arrays wird er schon lange verwendet, und seit ES2018 auch bei Objekten. In TypeScript ist Spread nicht nur eine syntaktische Verbesserung, sondern auch ein Werkzeug für eine präzise Typisierung bei der Bearbeitung von Objekten.
Historisch gesehen wurde das Klonen oder Erweitern eines Objekts in JavaScript durch Object.assign durchgeführt, was jedoch leicht zu einem Verlust der Typensicherheit und zu gefährlichen Schlüsselüberschneidungen führen konnte, wenn die Objektstruktur komplex war.
Das Problem besteht darin, dass TypeScript beim Verwenden von Spread zur Kombination/Erweiterung von Objekten einen neuen Typ basierend auf den Eingabestrukturen ableitet und mögliche Schlüsselkonflikte behandelt („der Letzte gewinnt“), was jedoch nicht immer das ist, was der Entwickler beabsichtigt hat. Besonderes Augenmerk sollte auf Überschneidungen mit optionalen Feldern, readonly und die Existenz privater Eigenschaften in Klassen gelegt werden.
Lösung: Verwenden Sie Spread, um dem TypeScript-Compiler zu ermöglichen, das Ergebnis automatisch abzuleiten. Für komplexe Fälle ist es wichtig, den Typ der Eingabestrukturen ausdrücklich einzuschränken und sorgfältig die Änderungen in der Typstruktur zu beobachten.
Beispielcode:
interface User { name: string; age: number; } interface Extra { isAdmin?: boolean; readonly city: string; } const base: User = { name: 'Ivan', age: 28 }; const extended: User & Extra = { ...base, city: 'Moscow', isAdmin: true };
Wesentliche Merkmale:
Kann man durch Spread-Vererbung eine "deep copy" eines Objekts in TypeScript erhalten?
Antwort: Nein. Spread führt nur eine flache Kopie (shallow copy) durch, verschachtelte Objekte bleiben über die Referenz bestehen.
const original = { user: { name: 'Anna' } }; const cloned = { ...original }; cloned.user.name = 'Maria'; // original.user.name ändert sich ebenfalls
Wird der Typ schrumpfen oder wachsen, wenn Objekte mit sich überschneidenden Schlüsseln mittels Spread kombiniert werden, und wie beeinflusst das die Typanmerkung der Variablen?
Antwort: Beim Spread wächst der Typ der Variablen, und im Falle von Schlüsselüberschneidungen gewinnen die Eigenschaften auf der rechten Seite; wenn es eine explizite Typanmerkung gibt, kann ein Fehler entstehen, wenn die Typen inkompatibel sind.
const a = { id: 4, value: "abc" }; const b = { value: 123 }; const c: { id: number; value: number } = { ...a, ...b }; // ok
Kann Spread auf Klassen angewendet werden und was passiert mit den privaten Eigenschaften?
Antwort: Spread ist nur auf öffentliche Eigenschaften der Klasse anwendbar. Private (private/#) und geschützte protected Felder gelangen nicht in das Resultatobjekt.
class Person { private id = 77; name = "Bob"; } const p = new Person(); const spreaded = { ...p }; // spreaded: { name: string }
Ein Entwickler macht Spread auf den Werten eines Objekts settings, um benutzerdefinierte Optionen zu erweitern:
const common = { theme: 'light', notifications: true }; const user = { notifications: false, signature: 'Sasha' }; const merged = { ...common, ...user };
Er erwartet, dass merged typkompatibel ist, erlaubt sich jedoch versehentlich einen Fehler in der Typanmerkung des Feldes signature und vergisst, dass Spread einfach Schlüssel kopiert und deren Werte nicht validiert.
Vorteile:
Nachteile:
Bei der Zusammenführung von Konfigurationen wird Spread nur nach der Validierung und mit einer Anmerkung für den Rückgabetyp verwendet:
interface Settings { theme: "light" | "dark"; notifications: boolean; signature?: string; } function getSettings(common: Settings, specific: Partial<Settings>): Settings { return { ...common, ...specific }; }
Vorteile:
Nachteile: