Mit aktivem strictNullChecks (Teil von strict) sind null und undefined eigene Typen. Ein string kann nie null sein; ein Wert, der fehlen könnte, muss das in seinem Typ sagen, etwa string | null, und TypeScript zwingt dich, diesen Fall zu behandeln, bevor du den Wert verwendest.
Nach der Prüfung weiß der Compiler, dass name ein string ist, also ist .split erlaubt. Der Rest dieser Seite behandelt die Wege, diese Prüfung zu machen, und die Operatoren, die sie kurz halten.
strictNullChecks und Fehler vom Typ „possibly null“
Enthält ein Typ null oder undefined, weigert sich TypeScript, den Wert zu verwenden, als wäre er immer da:
index.ts(3,22): error TS18047: 'name' is possibly 'null'.
Die Variante für undefined ist TS18048, 'x' is possibly 'undefined'. Ohne strictNullChecks sind null und undefined in jedem Typ erlaubt, und dieser Code kompiliert und stürzt dann zur Laufzeit mit einem TypeError ab, sobald name zum ersten Mal null ist. Diese Klasse von Bugs ist der Hauptgrund, strict anzulassen.
Woher diese Typen im Alltagscode kommen:
| Quelle | Typ |
|---|---|
arr.find(...) | T | undefined |
map.get(key) | V | undefined |
optionale Eigenschaft p?: T | T | undefined beim Lesen |
optionaler Parameter x?: T | T | undefined in der Funktion |
str.match(re) | RegExpMatchArray | null |
JSON.parse(text) | any, also wird nichts geprüft |
Auf null und undefined prüfen
Jede Prüfung unten engt den Typ im Block ein. Wähle die, die zu dem passt, was du ausschließen willst.
| Prüfung | Entfernt aus dem Typ |
|---|---|
x !== undefined | undefined |
x !== null | null |
x != null | null und undefined |
typeof x !== "undefined" | undefined |
if (x) | null und undefined, überspringt aber auch die Werte 0, "", false, NaN |
== null ist die eine Stelle, an der lose Gleichheit üblich ist: Es ist genau für null und undefined wahr, sonst nichts. Eine Prüfung auf Truthiness hätte den leeren String als fehlend behandelt, was oft ein Bug ist.
Optional Chaining: ?.
a?.b liest b, wenn a nicht null oder undefined ist, und hört andernfalls auf und gibt undefined zurück. Derselbe Operator funktioniert für Indizes, a?.[i], und für Aufrufe, fn?.().
Der Typ von bob.address?.city ist string | undefined: Optional Chaining fügt dem Ergebnis undefined hinzu, deshalb kombinierst du es meist mit ??. Das Laufzeitverhalten ist reines JavaScript; die Details zum Kurzschluss stehen unter Optional Chaining.
Das doppelte Fragezeichen: ??
a ?? b gibt a zurück, außer es ist null oder undefined, dann gibt es b zurück. Es ersetzt das ältere Idiom a || b, das auch 0, "", false und NaN verwirft:
| Linker Wert | left || "d" | left ?? "d" |
|---|---|---|
null | "d" | "d" |
undefined | "d" | "d" |
0 | "d" | 0 |
"" | "d" | "" |
false | "d" | false |
NaN | "d" | NaN |
Bei den Typen entfernt ?? null und undefined von der linken Seite und vereinigt den Rest mit der rechten Seite, deshalb lässt sich scores.get("Linus") ?? 0 einer number zuweisen.
Nullish Assignment: ??=
a ??= b weist a den Wert b nur zu, wenn a null oder undefined ist. Die Geschwister ||= und &&= weisen zu, wenn die linke Seite falsy beziehungsweise truthy ist.
retries: 0 übersteht ??=, während das leere label durch ||= ersetzt wird. Nach opts.retries ??= 3 engt TypeScript opts.retries für den Rest der Funktion auf number ein.
Optionale Eigenschaften vs | undefined
nickname?: string und nickname: string | undefined lesen sich gleich, unterscheiden sich aber darin, ob der Schlüssel existieren muss:
Nimm ?, wenn Aufrufer die Eigenschaft weglassen dürfen, und | undefined, wenn jeder Aufrufer sie ausdrücklich übergeben soll, auch wenn der Wert undefined ist. Die Option exactOptionalPropertyTypes (nicht Teil von strict) verschärft ? weiter: nickname?: string lehnt dann { nickname: undefined } ab und akzeptiert nur einen fehlenden Schlüssel oder einen String. Optionale Funktionsparameter (x?: number) verhalten sich wie optionale Eigenschaften: In der Funktion ist x vom Typ number | undefined.
null oder undefined: was verwenden
TypeScript erzwingt keine Wahl, aber beides in einer Codebasis zu mischen bedeutet, dass jede Prüfung zwei Fälle behandeln muss. Eine verbreitete Konvention:
- Nimm
undefined(und optionale Eigenschaften) für „nicht gesetzt“ in deinen eigenen Typen. Das erzeugt JavaScript standardmäßig: fehlende Eigenschaften, weggelassene Argumente, erfolglosefindundMap.get. - Akzeptiere
null, wo eine API es dir liefert:JSONkennt keinundefined,String.prototype.matchund viele DOM-Methoden gebennullzurück. - Prüfe mit
== null, wenn ein Wert beides sein kann.
Der Indexzugriff auf Arrays ist die eine Lücke: users[5] ist als Elementtyp typisiert, auch wenn der Index außerhalb des Bereichs liegt. Die Option noUncheckedIndexedAccess (nicht Teil von strict) fügt jedem Indexzugriff | undefined hinzu, damit der Compiler auch das findet.
Häufig gestellte Fragen
Was bedeutet das doppelte Fragezeichen in TypeScript?
a ?? b ist der Nullish-Coalescing-Operator aus JavaScript. Er gibt a zurück, außer a ist null oder undefined, dann gibt er b zurück. Anders als || behält er andere falsy Werte wie 0, "" und false. TypeScript entfernt null und undefined aus dem Typ der linken Seite, also ist a ?? "x" ein string, wenn a vom Typ string | undefined ist.
Wie prüfe ich in TypeScript, ob ein Wert undefined ist?
Vergleiche ihn: if (value !== undefined) { ... }. TypeScript engt den Typ im Block ein. Um null und undefined in einer Prüfung auszuschließen, nimm value != null (lose Gleichheit), die eine Stelle, an der ==/!= üblich ist. Eine Prüfung auf Truthiness (if (value)) engt ebenfalls ein, überspringt aber 0, "" und false.
Was bedeutet "Object is possibly undefined"?
Die Fehler TS18048 ('x' is possibly 'undefined') und TS18047 ('x' is possibly 'null') oder TS2532 (Object is possibly 'undefined'), wenn der Wert keinen einfachen Namen hat wie in getUser().address.city, kommen von strictNullChecks: Der Typ enthält undefined oder null, und der Code verwendet den Wert, als könnte er das nicht sein. Prüfe ihn zuerst, nutze Optional Chaining (x?.name), gib mit ?? einen Standardwert an oder ändere den Typ, wenn der Wert wirklich nicht fehlen kann.
Was ist der Unterschied zwischen null und undefined in TypeScript?
Es sind zwei getrennte Typen mit je einem Wert. JavaScript verwendet undefined für Dinge, die nie gesetzt wurden (eine fehlende Eigenschaft, ein weggelassenes Argument, Map.get auf einen fehlenden Schlüssel), und APIs verwenden null für ein absichtliches "kein Wert" (JSON, viele DOM-Methoden). TypeScript verfolgt sie getrennt, also akzeptiert string | null kein undefined. Viele Codebasen wählen undefined für ihren eigenen Code und akzeptieren null nur an Grenzen.
Ist eine optionale Eigenschaft dasselbe wie | undefined?
Nicht ganz. name?: string bedeutet, dass die Eigenschaft ganz fehlen darf, und beim Lesen ergibt sich string | undefined. name: string | undefined bedeutet, dass die Eigenschaft vorhanden sein muss, auch wenn ihr Wert undefined ist. Mit aktivem exactOptionalPropertyTypes akzeptiert name?: string außerdem keinen ausdrücklichen Wert undefined mehr.