null, undefined i bezpieczny dostęp

Brak wyniku to też informacja. Naucz się odróżniać brak od zera i pustego tekstu, zamiast dopisywać wykrzykniki, żeby uciszyć kompilator.

CEL LEKCJI

Po tej lekcji potrafisz

  • Opiszesz możliwość braku wartości w typie.
  • Użyjesz ?. i ?? zgodnie z ich znaczeniem.
  • Obsłużysz nieudane wyszukiwanie i wyjaśnisz ryzyko operatora !.
WPROWADZENIE

Gracza nie ma w rankingu

Szukasz nicku w tabeli wyników. Co powinien dostać program, jeśli taki gracz nie istnieje? Zero punktów oznacza coś innego niż brak gracza. Zamiast mieszać te sytuacje, pokażemy brak w typie.

POZNAJ SKŁADNIĘ

Małe kroki, konkretne efekty

Dwa sposoby oznaczania braku

undefined często pojawia się przy brakującym polu lub nieudanym wyszukiwaniu. null bywa celowym oznaczeniem „nie wybrano”. To konwencje — reguły konkretnego programu trzeba określić. W trybie strict nie udajemy, że te wartości są zwykłym tekstem.

src/index.ts — osobny przykład
function wybranyNick(nick: string | null): string {
  if (nick === null) { return "Wybierz gracza"; }
  return nick.toUpperCase();
}
console.log(wybranyNick(null));
console.log(wybranyNick("pixel"));
wynik w konsoli
Wybierz gracza
PIXEL

Osobna gałąź dla null pozwala bezpiecznie użyć metody tekstowej w pozostałej części funkcji.

Opcjonalny łańcuch i wartość zastępcza

?. zatrzymuje dostęp, gdy wartość po jego lewej stronie jest null albo undefined, i daje undefined. Operator ?? wybiera prawą stronę tylko przy tych dwóch wartościach. Nie odrzuca zera ani false.

src/index.ts — osobny przykład
interface Profil { staty?: { punkty: number } }
function punkty(p: Profil): number { return p.staty?.punkty ?? 0; }
console.log(punkty({}));
console.log(punkty({ staty: { punkty: 0 } }));
console.log(punkty({ staty: { punkty: 12 } }));
wynik w konsoli
0
0
12

Tutaj świadomie wyświetlamy 0 również przy braku statystyk. Jeśli musisz odróżniać te sytuacje, zwróć number | undefined zamiast stosować zastępstwo.

PRZYKŁAD Z OMÓWIENIEM

Wyszukiwarka wyników

find zwraca pierwszy pasujący element albo undefined. Dwa poprawne komunikaty odróżnią gracza z zerem punktów od osoby spoza rankingu.

src/index.ts — osobny przykład
interface Gracz { nick: string; punkty: number }
const gracze: Gracz[] = [{ nick: "Ala", punkty: 0 }, { nick: "Pixel", punkty: 15 }];
function wynik(nick: string): string {
  const gracz = gracze.find(g => g.nick === nick);
  if (gracz === undefined) { return nick + ": brak w rankingu"; }
  return gracz.nick + ": " + gracz.punkty + " pkt";
}
console.log(wynik("Ala"));
console.log(wynik("Neo"));
wynik w konsoli
Ala: 0 pkt
Neo: brak w rankingu

Jak program dochodzi do wyniku

  1. Callback find sprawdza nick każdego elementu do pierwszego dopasowania.
  2. Wynik ma typ Gracz | undefined.
  3. Warunek rozpoznaje brak obiektu, a nie fałszywość jego punktów.
  4. Po return w gałęzi braku gracz jest już pewnym obiektem.

Zanim przejdziesz dalej

Dlaczego if (!gracz?.punkty) jest złym testem istnienia gracza?

Sprawdź swoje rozumowanie

Da true również dla istniejącego gracza z zerem punktów. Sprawdzaj istnienie obiektu, a nie prawdziwość liczby.

EKSPERYMENT

Zmień kod i zobacz, co się stanie

Wykrzyknik nie jest sprawdzeniem

Operator ! po wartości usuwa null i undefined tylko z widoku kompilatora. Nie wstawia żadnego warunku. Bezpieczniejszy kod pokazuje, co zrobić po pustym wyszukiwaniu.

src/index.ts — osobny przykład
const wyniki = [10, 20];
const znaleziony = wyniki.find(n => n > 100);
console.log(znaleziony?.toFixed(1) ?? "Nie znaleziono");
// znaleziony!.toFixed(1) spowodowałoby błąd w czasie wykonania.
wynik w konsoli
Nie znaleziono

Zmień próg na 15: zobaczysz 20.0. ?. chroni przed brakiem, ale nie przechwytuje wyjątków rzuconych przez samą wywołaną metodę.

CZĘSTE BŁĘDY

Gdy wynik zaskakuje

Pomyłka Co się dzieje i jak to poprawić
|| zamiast ?? przy liczbach Zero jest fałszywe dla ||, ale często jest poprawną wartością.
?. traktowane jak naprawa danych Operator daje undefined. Nadal musisz ustalić, jak program reaguje na brak.
! zamiast warunku Wykrzyknik nie zapobiega błędowi wykonania.
ZADANIA

Teraz Twoja kolej

Rozwiązanie jest jedną z możliwości. Zanim je odkryjesz, uruchom własną wersję dla podanych danych i przynajmniej jednego przypadku brzegowego.

ZAD 1Domyślna głośność★☆☆

Funkcja ma przyjmować number | undefined. Brak daje 50, a 0 ma pozostać 0.

Mała podpowiedź

Użyj ??, nie ||.

Przykładowe rozwiązanie z wyjaśnieniem
src/index.ts — osobny przykład
function glosnosc(n: number | undefined): number { return n ?? 50; }
console.log(glosnosc(undefined), glosnosc(0));
wynik w konsoli
50 0

Wartość zastępcza nie odbiera możliwości wyciszenia dźwięku.

ZAD 2Ostatni wynik★★☆

Zwróć tekst ostatniego elementu tablicy albo „Brak rund”. Sprawdź [3, 8] oraz [].

Mała podpowiedź

at(-1) i opcjonalne toString.

Przykładowe rozwiązanie z wyjaśnieniem
src/index.ts — osobny przykład
function ostatni(rundy: number[]): string {
  return rundy.at(-1)?.toString() ?? "Brak rund";
}
console.log(ostatni([3, 8]));
console.log(ostatni([]));
wynik w konsoli
8
Brak rund

at może zwrócić undefined, także gdy tablica ma typ number[].

ZAD 3Miasto profilu★★☆

Profil może nie mieć adresu, a adres miasta. Zwróć miasto lub „Nie podano”. Sprawdź obie sytuacje.

Mała podpowiedź

Pytajnik przy polu w typie i ?. przy odczycie.

Przykładowe rozwiązanie z wyjaśnieniem
src/index.ts — osobny przykład
interface Profil { adres?: { miasto?: string } }
function miasto(p: Profil): string { return p.adres?.miasto ?? "Nie podano"; }
console.log(miasto({}));
console.log(miasto({ adres: { miasto: "Gdynia" } }));
wynik w konsoli
Nie podano
Gdynia

Brak adresu nie prowadzi do odczytu pola z undefined.

ZAD 4Bezpieczna średnia★★★

Pusta lista ocen ma zwracać null. W raporcie wypisz „Brak ocen”; [4, 5] ma dać 4.50. Oddziel funkcję obliczającą od raportu.

Mała podpowiedź

number | null opisuje dwie możliwości wyniku.

Przykładowe rozwiązanie z wyjaśnieniem
src/index.ts — osobny przykład
function srednia(oceny: number[]): number | null {
  if (oceny.length === 0) { return null; }
  return oceny.reduce((s, n) => s + n, 0) / oceny.length;
}
// reduce dodaje elementy do sumy zaczynającej od 0; szerzej w lekcji 23.
function raport(oceny: number[]): string { return srednia(oceny)?.toFixed(2) ?? "Brak ocen"; }
console.log(raport([]));
console.log(raport([4, 5]));
wynik w konsoli
Brak ocen
4.50

Brak danych nie jest sztuczną oceną zero.

PODSUMOWANIE

Co zabierasz do następnej lekcji

  • Brak wartości warto pokazać w typie.
  • ?. daje bezpieczny dostęp przy null i undefined; ?? dobiera zastępstwo.
  • ! jest zapewnieniem dla kompilatora, a nie ochroną podczas działania.