Unie i zawężanie typu
Operacja może się udać albo zakończyć błędem. Opisz oba warianty, zamiast tworzyć obiekt z przypadkowym zestawem pustych pól.
Po tej lekcji potrafisz
- Opiszesz wybór między typami przez unię.
- Zawęzisz typ przez typeof, in i wyróżnik.
- Zbudujesz własny strażnik typu i zrozumiesz odpowiedzialność za jego wynik.
Czy już mamy wynik meczu?
Przed meczem nie ma zwycięzcy. Po meczu jest wynik, a po odwołaniu — powód. Typ z samymi opcjonalnymi polami pozwoliłby pomieszać te stany. Unia umożliwia zapisanie kilku konkretnych wariantów.
Małe kroki, konkretne efekty
Pionowa kreska daje wybór
number | string oznacza jedną wartość, która może być liczbą lub tekstem. Nie możesz bez sprawdzenia używać metod dostępnych tylko dla jednego wariantu.
function opisId(id: number | string): string {
if (typeof id === "number") { return "Numer " + id.toFixed(0); }
return "Kod " + id.toUpperCase();
}
console.log(opisId(12));
console.log(opisId("ab"));Numer 12
Kod ABW gałęzi if kompilator wie, że id jest liczbą. Po return zostaje drugi wariant. To zawężanie: nie zmieniamy wartości, tylko ustalamy, który typ pasuje w tym miejscu.
Wyróżnik pozwala powiązać dane
Wspólne pole z różnymi stałymi tekstowymi nazywamy wyróżnikiem. Porównanie statusu podpowiada kompilatorowi, jakie pozostałe pola są dostępne.
type Odpowiedz =
| { status: "ok"; punkty: number }
| { status: "blad"; powod: string };
function pokaz(o: Odpowiedz): string {
if (o.status === "ok") { return "Punkty: " + o.punkty; }
return "Błąd: " + o.powod;
}
console.log(pokaz({ status: "blad", powod: "Mecz odwołany" }));Błąd: Mecz odwołanyTo precyzyjniejsze niż punkty?: number i powod?: string w jednym obiekcie. Wariant ok wymaga punktów, a wariant blad wymaga powodu.
Panel meczu w konsoli
Trzy stany mają różne dane. Użyjemy switch, aby każdy stan dostał własny komunikat.
type Mecz =
| { stan: "plan"; godzina: string }
| { stan: "koniec"; gospodarz: number; gosc: number }
| { stan: "odwolany"; powod: string };
function komunikat(mecz: Mecz): string {
switch (mecz.stan) {
case "plan": return "Start: " + mecz.godzina;
case "koniec": return mecz.gospodarz + ":" + mecz.gosc;
case "odwolany": return "Odwołano: " + mecz.powod;
}
}
const mecze: Mecz[] = [
{ stan: "plan", godzina: "16:00" },
{ stan: "koniec", gospodarz: 3, gosc: 2 },
{ stan: "odwolany", powod: "Brak prądu" }
];
for (const mecz of mecze) { console.log(komunikat(mecz)); }Start: 16:00
3:2
Odwołano: Brak prąduJak program dochodzi do wyniku
- Mecz to unia trzech kształtów obiektów. Każdy ma pole stan.
- Każdy case zawęża cały obiekt, a nie tylko tekst stan.
- return kończy funkcję, więc nie potrzebujemy break.
- Typ tablicy pozwala trzymać wszystkie warianty i przekazać każdy do tej samej funkcji.
Zanim przejdziesz dalej
Dlaczego w case „plan” nie odczytujemy mecz.gospodarz?
Sprawdź swoje rozumowanie
W tym stanie wynik nie istnieje. Kompilator pilnuje, żeby nie wyświetlać wyniku przed rozegraniem meczu.
Zmień kod i zobacz, co się stanie
in i własny strażnik
Jeżeli warianty nie mają wspólnego wyróżnika, możesz sprawdzić obecność pola. Zapis x is Kot informuje kompilator, co oznacza true. Musisz napisać zgodny z tą obietnicą warunek.
type Kot = { miaucz: () => string };
type Pies = { szczekaj: () => string };
function czyKot(x: Kot | Pies): x is Kot { return "miaucz" in x; }
function glos(x: Kot | Pies): string {
if (czyKot(x)) { return x.miaucz(); }
return x.szczekaj();
}
console.log(glos({ miaucz: () => "Miau" }));
console.log(glos({ szczekaj: () => "Hau" }));Miau
HauTen strażnik zakłada wejście już zgodne z Kot | Pies. Samo in nie sprawdza, czy pole jest funkcją. Nie stosuj go jako kompletnej walidacji nieznanego JSON-u; do tego wrócimy w lekcji 29.
Gdy wynik zaskakuje
| Pomyłka | Co się dzieje i jak to poprawić |
|---|---|
| Unia traktowana jak „wszystkie pola naraz” | Wartość ma jeden z dopuszczonych kształtów. Najpierw rozpoznaj wariant. |
| Strażnik zawsze zwracający true | Kompilator ufa deklaracji is. Błędna implementacja może doprowadzić do awarii. |
| typeof null | Daje „object”. Przy nieznanych danych osobno wyklucz null. |
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.
Przyjmij number | string. Liczbę poprzedź #, a tekst zamień na wielkie litery. Sprawdź 5 i „ab”.
Mała podpowiedź
typeof rozdziela dwa warianty.
Przykładowe rozwiązanie z wyjaśnieniem
function kod(x: number | string): string {
return typeof x === "number" ? "#" + x : x.toUpperCase();
}
console.log(kod(5), kod("ab"));#5 ABKażda gałąź zwraca tekst, mimo różnych typów wejścia.
Opisz rolę jako „gracz” | „moderator”. Funkcja ma zwracać true tylko dla moderatora.
Mała podpowiedź
Unia literałów zamyka listę dopuszczalnych tekstów.
Przykładowe rozwiązanie z wyjaśnieniem
type Rola = "gracz" | "moderator";
function mozeUsunac(rola: Rola): boolean { return rola === "moderator"; }
console.log(mozeUsunac("gracz"), mozeUsunac("moderator"));false trueLiterówka w roli zostanie wykryta podczas sprawdzania kodu.
Zbuduj unię z wyróżnikiem ok: true albo false. Sukces ma numer, błąd ma powod. Wypisz oba przypadki.
Mała podpowiedź
Wyróżnik nie musi być tekstem.
Przykładowe rozwiązanie z wyjaśnieniem
type Platnosc = { ok: true; numer: number } | { ok: false; powod: string };
function opis(p: Platnosc): string {
if (p.ok) { return "Przyjęto " + p.numer; }
return "Odmowa: " + p.powod;
}
console.log(opis({ ok: true, numer: 7 }));
console.log(opis({ ok: false, powod: "Brak monet" }));Przyjęto 7
Odmowa: Brak monetNie da się poprawnie opisać sukcesu bez numeru — tego wymaga wariant typu.
Film ma rodzaj „film” i minuty, gra rodzaj „gra” i poziomy. Przygotuj wspólną funkcję opisu i tablicę obu obiektów.
Mała podpowiedź
switch po polu rodzaj.
Przykładowe rozwiązanie z wyjaśnieniem
type Media = { rodzaj: "film"; minuty: number } | { rodzaj: "gra"; poziomy: number };
function opis(m: Media): string {
switch (m.rodzaj) {
case "film": return m.minuty + " minut";
case "gra": return m.poziomy + " poziomów";
}
}
const biblioteka: Media[] = [{ rodzaj: "film", minuty: 90 }, { rodzaj: "gra", poziomy: 12 }];
biblioteka.forEach(m => console.log(opis(m)));90 minut
12 poziomówWspólny typ pozwala przechowywać różne media, a wyróżnik utrzymuje poprawne połączenia pól.
Co zabierasz do następnej lekcji
- Unia opisuje wybór; zawężanie ustala pasujący wariant.
- Wyróżnik łączy stan z właściwymi dla niego danymi.
- Własny strażnik jest obietnicą, której poprawność musisz sprawdzić.