Das Problem: Verschachtelte Zugriffe sind fragil
Auf ein verschachteltes Objekt zuzugreifen funktioniert wunderbar – bis eine der Ebenen plötzlich fehlt:
user.address ist undefined, und der Zugriff auf .city bei undefined wirft TypeError: Cannot read properties of undefined. In der Praxis – sei es bei API-Antworten, geparstem JSON oder DOM-Abfragen – hat man ständig mit Feldern zu tun, die mal da sind und mal nicht. Für jede Ebene eine eigene Absicherung zu schreiben, wird schnell unübersichtlich:
const city = user && user.address && user.address.city;
Mit zwei Ebenen geht's gerade noch. Bei vier Ebenen wird's zur Qual. Der ?.-Operator ist hier die elegantere Lösung.
Optional Chaining in JavaScript: ?. bricht bei null und undefined ab
Mit Optional Chaining greifst du nur dann auf eine Eigenschaft zu, wenn der Wert davor weder null noch undefined ist. Andernfalls liefert der gesamte Ausdruck undefined zurück – und die Auswertung stoppt sofort.
Zeile eins liest name ganz normal aus. Zeile zwei trifft auf user.address, sieht undefined und steigt aus – der Rest der Kette wird gar nicht mehr ausgeführt. Zeile drei würde ohne ?. einen Fehler werfen, weil .toUpperCase() auf undefined aufgerufen würde. Mit ?. ergibt der gesamte Ausdruck einfach stillschweigend undefined.
Das mentale Modell dahinter: ?. bedeutet "wenn das, was vor mir steht, null oder undefined ist, breche ich ab und gebe undefined zurück – ansonsten mache ich weiter."
Optional Chaining bei Arrays und Funktionsaufrufen
Drei Schreibweisen, dieselbe Idee:
?.[...]für den Array-Zugriff oder dynamische Properties.?.()für den Aufruf von etwas, das vielleicht eine Funktion ist – oder eben nicht.?.namefür den normalen Property-Zugriff.
Alle drei brechen nach demselben Prinzip ab. user?.notAMethod?.() wirft keinen Fehler, obwohl notAMethod gar nicht existiert – der zweite ?. sieht undefined und stoppt einfach.
Besonders praktisch ist das bei optionalen Callbacks:
Schluss mit if (typeof onDone === "function")-Wächtern vor jedem einzelnen Aufruf.
Nur null und undefined lösen den Kurzschluss aus
Das ist genau der Punkt, an dem viele ins Stolpern geraten. ?. reagiert ausschließlich auf nullish-Werte. Andere falsy-Werte — 0, "", false, NaN — sind völlig legitime Kandidaten für die Verkettung (soweit Autoboxing das eben zulässt):
data.count ist 0 – also falsy, aber eben nicht nullish. Deshalb wird ?.toFixed(2) ganz normal ausgeführt und liefert "0.00" zurück. Zum Vergleich mal dasselbe mit &&:
Die &&-Variante gibt 0 zurück, weil data.count falsy ist und der Ausdruck per Short-Circuit abbricht. Die ?.-Variante liefert hingegen "0.00", da 0 kein Nullish-Wert ist. Wenn du wirklich auch bei 0 stoppen möchtest, ist && die richtige Wahl. Geht es dir aber nur um den Fall „der Wert fehlt", dann brauchst du ?..
Die Position des ? ist entscheidend
?. sichert den Wert davor ab – nicht das, was danach kommt. Setz das Fragezeichen also genau auf die Ebene, die möglicherweise fehlt:
Alle drei Varianten funktionieren hier, weil tatsächlich nichts fehlt. Sobald aber config.server undefined sein könnte, brauchst du config.server?.host – ein ?. vor server würde nichts bringen, denn das Problem ist ja, dass .host aus einem fehlenden server gelesen wird.
Eine gute Faustregel: Setze ?. genau an den Stellen, an denen der Wert davor realistisch nullish sein kann. Wer ?. vorsorglich an jeden Punkt klatscht, versteckt Bugs und bläht den Code unnötig auf.
Zuweisungen über ?. funktionieren nicht
Optional Chaining ist read-only. Auf der linken Seite einer Zuweisung kannst du es nicht verwenden:
user?.address?.city = "Paris"; // SyntaxError
Das ergibt auch Sinn, wenn man kurz darüber nachdenkt – was sollte es schon heißen, einer Eigenschaft von undefined einen Wert zuzuweisen? Wenn du einen Wert nur dann setzen willst, falls das übergeordnete Objekt existiert, musst du das explizit ausschreiben:
Wann lohnt sich Optional Chaining – und wann nicht?
Der ?.-Operator spielt seine Stärken aus, wenn ein Wert tatsächlich optional sein darf:
- API-Antworten, bei denen einzelne Felder mal vorhanden sind und mal fehlen.
- DOM-Zugriffe:
document.querySelector(".banner")?.remove(). - Callbacks, die nicht zwingend übergeben werden müssen:
options.onError?.(err). - Verkettete Bibliotheksaufrufe, bei denen Zwischenergebnisse
nullsein können.
Falsch eingesetzt ist Optional Chaining immer dann, wenn der Wert eigentlich immer vorhanden sein müsste. Wer in solchen Fällen überall ?. streut, um Fehler stumm zu schalten, macht aus einem lauten, gut auffindbaren Bug ("TypeError in Zeile 42") ein leises Problem – eine Variable, die drei Funktionen weiter plötzlich undefined ist. Wenn etwas da sein muss, soll es ruhig krachen: Der Stacktrace tut dir einen Gefallen.
Ein Beispiel aus der Praxis
Einen Wert aus einer möglicherweise unvollständigen API-Antwort auslesen:
Jedes ?. kümmert sich um genau eine Unsicherheits-Ebene. avatarUrl wird sauber zu undefined. Und onClick?.() ruft den Handler nur dann auf, wenn es ihn überhaupt gibt.
Dir ist sicher das ?? in der Zeile mit displayName aufgefallen — das ist die zweite Hälfte dieses Patterns. ?. liefert dir undefined, wenn etwas fehlt; mit ?? setzt du einen Default, ohne dabei über legitime falsy-Werte wie 0 oder "" zu stolpern.
Als Nächstes: Nullish Coalescing
?. und ?? sind im Grunde dieselbe Idee, nur auf unterschiedliche Probleme angewendet: Beide behandeln null und undefined als "fehlt", lassen andere falsy-Werte aber in Ruhe. Im nächsten Abschnitt schauen wir uns an, wie ?? dir vernünftige Default-Werte liefert — und warum || genau das seit Jahren still und heimlich falsch macht.
Häufig gestellte Fragen
Was macht ?. in JavaScript eigentlich?
Der ?.-Operator greift nur dann auf eine Property, einen Array-Index oder eine Methode zu, wenn der Wert davor nicht null oder undefined ist. Ist er es doch, bricht der ganze Ausdruck kurz ab und liefert undefined zurück, statt einen Fehler zu werfen. user?.address?.city knallt also nicht, wenn user oder address fehlt.
Wann sollte man Optional Chaining verwenden?
Immer dann, wenn ein Wert legitim fehlen darf – API-Antworten mit optionalen Feldern, DOM-Abfragen, die ins Leere laufen können, Callbacks, die nicht zwingend mitgegeben werden. Was du damit nicht tun solltest: Bugs kaschieren, bei denen ein Wert eigentlich immer da sein müsste. In solchen Fällen ist das fehlende Feld nämlich ein wichtiges Signal, das du nicht unter den Teppich kehren willst.
Was ist der Unterschied zwischen ?. und &&?
In den meisten Fällen kommt dasselbe raus, aber ?. bricht nur bei null oder undefined ab, während && bei jedem falsy Wert kurzschließt – also auch bei 0, '' und false. obj && obj.count gibt zwar 0 zurück, wenn count gleich 0 ist, aber das kann zum Problem werden, sobald Zwischenwerte im Spiel sind. obj?.count liefert ebenfalls 0, stoppt die Kette aber sauber ausschließlich bei nullish-Werten.
Funktioniert Optional Chaining auch mit Arrays und Funktionsaufrufen?
Ja. Mit arr?.[0] liest du sicher einen Array-Index, mit fn?.() rufst du eine Funktion nur dann auf, wenn sie existiert. Beides folgt derselben Regel: Ist der Wert vor ?. gleich null oder undefined, wird der Ausdruck zu undefined und es passiert sonst nichts.