any, unknown, never i asercje

Kompilator pomaga tak długo, jak długo dostaje prawdziwe informacje. Sprawdź, kiedy przyznać „jeszcze nie wiem”, a kiedy opisać sytuację niemożliwą.

CEL LEKCJI

Po tej lekcji potrafisz

  • Wyjaśnisz różnicę między any i unknown.
  • Sprawdzisz nieznaną wartość przed użyciem.
  • Rozpoznasz zastosowania never i ograniczenia asercji as.
WPROWADZENIE

Paczka danych bez etykiety

Ktoś przekazuje wynik ankiety, ale nie wiesz jeszcze, czy jest liczbą, tekstem czy obiektem. any pozwala zachowywać się tak, jakby wszystko było dozwolone. unknown wymaga otwarcia paczki i sprawdzenia zawartości.

POZNAJ SKŁADNIĘ

Małe kroki, konkretne efekty

unknown wymusza pytanie o wartość

Do unknown można przypisać dowolną wartość, lecz nie można na ślepo wywoływać jej metod. any wyłącza wiele zabezpieczeń i łatwo przenosi tę niepewność do kolejnych zmiennych.

src/index.ts — osobny przykład
function pokaz(dane: unknown): string {
  if (typeof dane === "string") { return dane.trim(); }
  if (typeof dane === "number") { return dane.toFixed(1); }
  return "Nieobsługiwany rodzaj danych";
}
console.log(pokaz("  Ala  "));
console.log(pokaz(3));
console.log(pokaz(null));
wynik w konsoli
Ala
3.0
Nieobsługiwany rodzaj danych

Po sprawdzeniu typeof nie potrzebujemy as. Kompilator zna już typ w odpowiedniej gałęzi.

as nie konwertuje

Asercja mówi kompilatorowi, jak ma traktować wyrażenie. Nie zamienia tekstu na liczbę i nie sprawdza pól obiektu. Do rzeczywistej konwersji potrzebna jest operacja taka jak Number.

src/index.ts — osobny przykład
const nieznane: unknown = "42";
const zapewnienie = nieznane as number;
console.log(typeof zapewnienie);
console.log(Number(nieznane) + 1);
wynik w konsoli
string
43

Mimo typu number wpisanego przez as, działający program nadal ma tekst. Podwójna asercja przez unknown nie naprawia danych — tylko obchodzi sprawdzanie.

PRZYKŁAD Z OMÓWIENIEM

Odczyt punktów z nieznanej wartości

Przyjmujemy wyłącznie skończone, nieujemne liczby całkowite. Typ number obejmuje także ułamki, NaN i Infinity, więc sam typeof to za mało.

src/index.ts — osobny przykład
function odczytajPunkty(dane: unknown): number | null {
  if (typeof dane !== "number") { return null; }
  if (!Number.isFinite(dane) || !Number.isInteger(dane) || dane < 0) { return null; }
  return dane;
}
const proby: unknown[] = [12, "12", -1, 1.5, NaN, Infinity];
for (const proba of proby) {
  console.log(odczytajPunkty(proba) ?? "Błędne dane");
}
wynik w konsoli
12
Błędne dane
Błędne dane
Błędne dane
Błędne dane
Błędne dane

Jak program dochodzi do wyniku

  1. unknown[] pozwala przygotować różne rodzaje danych do sprawdzenia.
  2. Pierwszy warunek odrzuca wszystko poza liczbami.
  3. Pozostałe warunki sprawdzają reguły gry. Kompilator sam nie odgadnie tych wymagań.
  4. null sygnalizuje odrzucenie. Nie mylimy go z prawidłowym zerem.

Zanim przejdziesz dalej

Czy „12” zostanie zamienione na 12?

Sprawdź swoje rozumowanie

Nie. Ta funkcja waliduje, ale nie konwertuje. Dzięki temu dane o złym rodzaju nie są po cichu akceptowane.

EKSPERYMENT

Zmień kod i zobacz, co się stanie

never pilnuje wszystkich wariantów

never opisuje wartość, która nie powinna móc wystąpić. Po obsłużeniu wszystkich wariantów unii nie zostaje żaden. Funkcja z throw nigdy nie wraca normalnie; szczegóły wyjątków poznasz w lekcji 26.

src/index.ts — osobny przykład
type Tryb = "solo" | "duet";
function niemozliwe(x: never): never { throw new Error("Nieznany tryb: " + x); }
function limit(tryb: Tryb): number {
  switch (tryb) {
    case "solo": return 1;
    case "duet": return 2;
    default: return niemozliwe(tryb);
  }
}
console.log(limit("solo"), limit("duet"));
wynik w konsoli
1 2

Dopisz „squad” do Tryb bez nowego case. Kompilator zgłosi, że do never przekazujesz nieobsłużony wariant. void oznacza brak użytecznego wyniku, a never — brak normalnego zakończenia z wynikiem.

CZĘSTE BŁĘDY

Gdy wynik zaskakuje

Pomyłka Co się dzieje i jak to poprawić
any jako lekarstwo na czerwone podkreślenia Najpierw ustal model danych. any może ukryć faktyczny błąd.
as traktowane jak walidacja Asercja nie tworzy warunków wykonywanych przez program.
typeof n === „number” jako pełna kontrola NaN i Infinity też mają ten typ. Reguły dziedziny sprawdzaj osobno.

throw w strażniku typu

Instrukcja throw przerywa program i zgłasza błąd. Używamy jej tu jako najkrótszej reakcji na dane, które nie przeszły sprawdzenia. Jak taki błąd przechwycić i zareagować zamiast kończyć program, pokazuje lekcja 26.

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 1Nieznany nick★☆☆

Przyjmij unknown, zwróć wielkie litery dla tekstu, a dla reszty „Niepoprawny nick”.

Mała podpowiedź

typeof przed toUpperCase.

Przykładowe rozwiązanie z wyjaśnieniem
src/index.ts — osobny przykład
function nick(x: unknown): string { return typeof x === "string" ? x.toUpperCase() : "Niepoprawny nick"; }
console.log(nick("pixel"));
console.log(nick(12));
wynik w konsoli
PIXEL
Niepoprawny nick

Nie trzeba niczego rzutować.

ZAD 2Brama turnieju★★☆

Akceptuj tylko całkowitą liczbę uczestników 1–16. Sprawdź 0, 1, 16, 17, „4” i 2.5.

Mała podpowiedź

Po typeof dodaj Number.isInteger i zakres.

Przykładowe rozwiązanie z wyjaśnieniem
src/index.ts — osobny przykład
function poprawnaLiczba(x: unknown): boolean {
  return typeof x === "number" && Number.isInteger(x) && x >= 1 && x <= 16;
}
for (const x of [0, 1, 16, 17, "4", 2.5]) { console.log(poprawnaLiczba(x)); }
wynik w konsoli
false
true
true
false
false
false

Granice należą do poprawnego zakresu, ułamki nie.

ZAD 3Rzutowanie kontra konwersja★★☆

Wykaż, że as number nie zmienia tekstu „7”. Wypisz typeof po asercji i po Number.

Mała podpowiedź

Wartość startowa unknown pozwala napisać asercję — nie oznacza to, że jest prawdziwa.

Przykładowe rozwiązanie z wyjaśnieniem
src/index.ts — osobny przykład
const dane: unknown = "7";
const a = dane as number;
const b = Number(dane);
console.log(typeof a, typeof b);
wynik w konsoli
string number

Typ statyczny i rodzaj wartości podczas wykonania mogą się rozminąć po nieprawdziwej asercji.

ZAD 4Kompletność statusów★★★

Obsłuż „nowe”, „w_toku”, „gotowe” i dodaj kontrolę never. Potem dopisz nowy status i sprawdź błąd kompilatora.

Mała podpowiedź

W default przekaż status do funkcji przyjmującej never.

Przykładowe rozwiązanie z wyjaśnieniem
src/index.ts — osobny przykład
type Status = "nowe" | "w_toku" | "gotowe";
function brak(x: never): never { throw new Error(String(x)); }
function opis(s: Status): string {
  switch (s) {
    case "nowe": return "Czeka";
    case "w_toku": return "Praca trwa";
    case "gotowe": return "Zrobione";
    default: return brak(s);
  }
}
console.log(opis("gotowe"));
wynik w konsoli
Zrobione

Zmiana modelu wymusi uzupełnienie zależnej funkcji.

PODSUMOWANIE

Co zabierasz do następnej lekcji

  • unknown przechowuje nieznane dane bez rezygnowania z kontroli.
  • as nie konwertuje i nie sprawdza wartości.
  • never pomaga wykryć nieobsłużony wariant.