Menu

null i undefined w TypeScript: sprawdzanie oraz operatory ?. i ??

Przy strictNullChecks null i undefined to osobne typy, które TypeScript każe ci obsłużyć. Zobacz, jak je sprawdzać, jak działa optional chaining (?.), podwójny znak zapytania (??) i ??=, oraz czym różni się właściwość opcjonalna od | undefined.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

Przy włączonym strictNullChecks (to część strict) null i undefined mają własne typy. string nigdy nie może być null; wartość, której może brakować, musi to zaznaczyć w swoim typie, na przykład string | null, a TypeScript każe ci obsłużyć ten przypadek przed użyciem wartości.

Po sprawdzeniu kompilator wie, że name to string, więc .split jest dozwolone. Reszta strony omawia sposoby takiego sprawdzania i operatory, które je skracają.

strictNullChecks i błędy "possibly null"

Gdy typ zawiera null lub undefined, TypeScript nie pozwala używać wartości tak, jakby zawsze istniała:

index.ts(3,22): error TS18047: 'name' is possibly 'null'.

Wersja dla undefined to TS18048, 'x' is possibly 'undefined'. Bez strictNullChecks null i undefined są dozwolone w każdym typie i ten kod się kompiluje, a potem wywala się w czasie działania z TypeError, gdy tylko name okaże się null. Ta klasa błędów to główny powód, by trzymać strict włączone.

Skąd te typy biorą się w codziennym kodzie:

ŹródłoTyp
arr.find(...)T | undefined
map.get(key)V | undefined
właściwość opcjonalna p?: TT | undefined przy odczycie
parametr opcjonalny x?: TT | undefined wewnątrz funkcji
str.match(re)RegExpMatchArray | null
JSON.parse(text)any, więc nic nie jest sprawdzane

Sprawdzanie null i undefined

Każde z poniższych sprawdzeń zawęża typ wewnątrz bloku. Wybierz to, które pasuje do tego, co chcesz wykluczyć.

SprawdzenieUsuwa z typu
x !== undefinedundefined
x !== nullnull
x != nullnull i undefined
typeof x !== "undefined"undefined
if (x)null i undefined, a do tego pomija wartości 0, "", false, NaN

== null to jedyne miejsce, gdzie luźna równość jest idiomatyczna: jest prawdziwe dokładnie dla null i undefined, dla niczego więcej. Sprawdzenie prawdziwości potraktowałoby pusty string jako brak wartości, co często jest błędem.

Optional chaining: ?.

a?.b odczytuje b, jeśli a nie jest null ani undefined, a w przeciwnym razie zatrzymuje się i zwraca undefined. Ten sam operator działa dla indeksów, a?.[i], i dla wywołań, fn?.().

Typ bob.address?.city to string | undefined: optional chaining dodaje undefined do wyniku, więc zwykle łączy się go z ??. Zachowanie w czasie działania to zwykły JavaScript; szczegóły skracania obliczeń znajdziesz w optional chaining.

Podwójny znak zapytania: ??

a ?? b zwraca a, chyba że to null lub undefined, wtedy zwraca b. Zastępuje starszy idiom a || b, który odrzuca też 0, "", false i NaN:

Lewa wartośćleft || "d"left ?? "d"
null"d""d"
undefined"d""d"
0"d"0
"""d"""
false"d"false
NaN"d"NaN

Na poziomie typów ?? usuwa null i undefined z lewej strony i łączy resztę w unię z prawą stroną, dlatego scores.get("Linus") ?? 0 można przypisać do number.

Nullish assignment: ??=

a ??= b przypisuje b do a tylko wtedy, gdy a to null lub undefined. Jego rodzeństwo, ||= i &&=, przypisuje, gdy lewa strona jest fałszywa lub prawdziwa.

retries: 0 przetrwa ??=, a pusty label zostaje zastąpiony przez ||=. Po opts.retries ??= 3 TypeScript zawęża opts.retries do number do końca funkcji.

Właściwości opcjonalne a | undefined

nickname?: string i nickname: string | undefined przy odczycie wyglądają tak samo, ale różnią się tym, czy klucz musi istnieć:

Użyj ?, gdy wywołujący mogą pominąć właściwość, a | undefined, gdy chcesz, żeby każdy wywołujący przekazał ją jawnie, nawet jeśli wartość to undefined. Opcja exactOptionalPropertyTypes (nie jest częścią strict) jeszcze bardziej zaostrza ?: wtedy nickname?: string odrzuca { nickname: undefined } i przyjmuje tylko brak klucza albo string. Opcjonalne parametry funkcji (x?: number) zachowują się jak właściwości opcjonalne: wewnątrz funkcji x ma typ number | undefined.

null czy undefined: czego używać

TypeScript nie wymusza wyboru, ale mieszanie obu w jednym projekcie oznacza, że każde sprawdzenie musi obsłużyć dwa przypadki. Popularna konwencja:

  • Używaj undefined (i właściwości opcjonalnych) dla "nieustawione" we własnych typach. To właśnie domyślnie produkuje JavaScript: brakujące właściwości, pominięte argumenty, nieudane find i Map.get.
  • Przyjmuj null tam, gdzie daje ci go API: JSON nie ma undefined, a String.prototype.match i wiele metod DOM zwraca null.
  • Sprawdzaj przez == null, gdy wartość może być jednym albo drugim.

Jedyną luką jest indeksowanie tablic: users[5] ma typ elementu, nawet gdy indeks wykracza poza zakres. Opcja noUncheckedIndexedAccess (nie jest częścią strict) dodaje | undefined do każdego dostępu przez indeks, więc kompilator wyłapuje także to.

Najczęściej zadawane pytania

Co oznacza podwójny znak zapytania w TypeScript?

a ?? b to operator nullish coalescing z JavaScriptu. Zwraca a, chyba że a to null lub undefined, wtedy zwraca b. W przeciwieństwie do || zachowuje inne wartości fałszywe, takie jak 0, "" i false. TypeScript usuwa null i undefined z typu lewej strony, więc gdy a ma typ string | undefined, a ?? "x" jest typu string.

Jak sprawdzić, czy wartość jest undefined w TypeScript?

Porównaj ją: if (value !== undefined) { ... }. TypeScript zawęża typ wewnątrz bloku. Aby jednym sprawdzeniem wykluczyć zarówno null, jak i undefined, użyj value != null (luźna równość), to jedyne miejsce, gdzie ==/!= jest idiomatyczne. Sprawdzenie prawdziwości (if (value)) też zawęża, ale pomija 0, "" i false.

Co oznacza "Object is possibly undefined"?

Błędy TS18048 ('x' is possibly 'undefined') i TS18047 ('x' is possibly 'null') albo TS2532 (Object is possibly 'undefined'), gdy wartość nie ma prostej nazwy, jak w getUser().address.city, pochodzą z strictNullChecks: typ zawiera undefined lub null, a kod używa wartości tak, jakby nie mogło jej brakować. Najpierw ją sprawdź, użyj optional chaining (x?.name), podaj wartość domyślną przez ?? albo zmień typ, jeśli wartości naprawdę nie może brakować.

Czym różni się null od undefined w TypeScript?

To dwa osobne typy, każdy z jedną wartością. JavaScript używa undefined dla rzeczy, które nigdy nie zostały ustawione (brakująca właściwość, pominięty argument, Map.get dla nieistniejącego klucza), a API używają null dla zamierzonego "braku wartości" (JSON, wiele metod DOM). TypeScript śledzi je osobno, więc string | null nie przyjmuje undefined. Wiele projektów wybiera undefined we własnym kodzie i przyjmuje null tylko na granicach.

Czy właściwość opcjonalna to to samo co | undefined?

Nie do końca. name?: string oznacza, że właściwości może w ogóle nie być, a jej odczyt daje string | undefined. name: string | undefined oznacza, że właściwość musi istnieć, nawet jeśli jej wartość to undefined. Z włączonym exactOptionalPropertyTypes name?: string przestaje też przyjmować jawną wartość undefined.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ