Menu

Typy unii w TypeScript: operator | z przykładami

Typ unii, taki jak string | number, oznacza, że wartość może mieć jeden z kilku typów. Zobacz, co można zrobić z unią (tylko to, co obsługuje każdy jej element), jak ją zawęzić, unie literałów i typów obiektów oraz różnicę między (A | B)[] a A[] | B[].

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

Typ unii wymienia warianty rozdzielone przez |: wartość typu string | number to string albo liczba. Unie to sposób, w jaki TypeScript opisuje wartości, które mogą legalnie przyjmować więcej niż jedną postać, a kompilator każe ci sprawdzić, z którą postacią masz do czynienia, zanim użyjesz czegoś, co dotyczy tylko jej.

W każdej gałęzi sprawdzenia typeof zmienna id ma jeden typ. Ten krok to zawężanie i to on sprawia, że unie są praktyczne.

Dozwolone są tylko wspólne składowe

Przed zawężeniem możesz używać tylko tego, co obsługuje każdy element unii. toString() istnieje zarówno w stringach, jak i w liczbach, więc jest w porządku, a toUpperCase() istnieje tylko w stringach:

index.ts(3,16): error TS2339: Property 'toUpperCase' does not exist on type 'string | number'.
  Property 'toUpperCase' does not exist on type 'number'.

Druga linia nazywa element, któremu brakuje tej właściwości. Ta sama reguła działa w drugą stronę: wartości string | number nie da się przekazać do parametru typu string (TS2345), bo może to być liczba. Unia przyjmuje więcej wartości, a w zamian pozwala zrobić z nimi mniej, dopóki ich nie sprawdzisz.

Zawężanie unii

Zawężanie używa zwykłych sprawdzeń z JavaScriptu. TypeScript śledzi przepływ sterowania i usuwa elementy, gdy zostają wykluczone, więc po ostatnim sprawdzeniu zostaje tylko jeden:

SprawdzenieZawężaDobre dla
typeof x === "string"do typu prostegostring, number, boolean, bigint, symbol, undefined, function
x === null, x === "a"do porównywanej wartościnull, undefined, elementy będące literałami
Array.isArray(x)do elementu tablicowegotablice
x instanceof Datedo klasyinstancje klas
"meow" in xdo elementów, które mają tę właściwośćtypy obiektów
x.kind === "circle"do elementu z tym znacznikiemdiscriminated unions
isCat(x) (zwraca x is Cat)do tego, co mówi funkcjacokolwiek, własna logika

Pełną listę form zawężania znajdziesz na stronie o zawężaniu typów.

Unie typów literałów

Unia wartości literałów to zamknięty zbiór dozwolonych wartości. To najczęstsza unia w prawdziwym kodzie:

type Status = "idle" | "loading" | "success" | "error";
type Dice = 1 | 2 | 3 | 4 | 5 | 6;
type Toggle = "on" | "off" | boolean; // boolean is itself true | false

let current: Status = "idle";
current = "loading";   // fine
current = "finished";  // error TS2322: Type '"finished"' is not assignable to type 'Status'.

Porównanie z literałem zawęża: po if (current === "error") gałąź else wie, że current to jedna z pozostałych trzech wartości. W wielu projektach unie literałów zastępują enumy. O as const i o tym, jak wyprowadzić taką unię z tablicy, przeczytasz na stronie o typach literałów.

Unie typów obiektów

Gdy elementy są typami obiektów, właściwości wspólne dla wszystkich są dostępne od razu. Dla reszty sprawdź przez in, czy właściwość istnieje:

Przy uniach kilku kształtów obiektów czytelniejszy jest wzorzec ze wspólną właściwością-literałem, taką jak kind: "cat" / kind: "fish". Sprawdzenie tej jednej właściwości zawęża cały obiekt, a switch po niej można sprawdzić pod kątem pełnego pokrycia. Ten wzorzec to discriminated union.

Tablice i unie

To, gdzie stoją nawiasy, całkowicie zmienia znaczenie:

TypOznaczaPrzykładowa wartość
(string | number)[]tablicę, której każdy element to string albo liczba[1, "two", 3]
string[] | number[]tablicę samych stringów albo tablicę samych liczb["a", "b"]
string | number[]string albo tablicę liczb (| wiąże słabiej niż [])"text"

Przy iterowaniu po (string | number)[] każdy element ma typ unii i wymaga zawężenia, jak w callbacku reduce powyżej. Metody takie jak map i filter działają też na string[] | number[], a callback dostaje string | number.

Unie z null i undefined

Najczęstsza unia ze wszystkich to "wartość albo nic": string | null, User | undefined. To właśnie zwracają Array.prototype.find i Map.prototype.get, a właściwość opcjonalna name?: string przy odczycie ma typ string | undefined. Obsługa takich wartości przez ?., ?? i sprawdzanie null ma własną stronę: null i undefined.

Żeby usunąć elementy z istniejącej unii na poziomie typów, użyj wbudowanych typów narzędziowych: Exclude<"a" | "b" | "c", "a"> to "b" | "c", a NonNullable<string | null> to string.

Najczęściej zadawane pytania

Czym jest typ unii w TypeScript?

To typ złożony z kilku wariantów połączonych przez |. Wartość typu string | number może być stringiem albo liczbą. Kompilator pozwala używać tylko tego, co wszystkie elementy mają wspólnego, dopóki nie zawęzisz wartości do jednego elementu sprawdzeniem takim jak typeof value === "string".

Dlaczego TypeScript mówi, że właściwość nie istnieje w typie unii?

Bo co najmniej jeden element unii jej nie ma. Błąd TS2339, na przykład Property 'toUpperCase' does not exist on type 'string | number', oznacza, że wartość może być liczbą, a liczba nie ma toUpperCase. Najpierw zawęź typ (typeof, in, Array.isArray, instanceof albo sprawdzenie znacznika), a potem użyj właściwości konkretnego elementu.

Jak zadeklarować tablicę, która przechowuje więcej niż jeden typ?

Umieść unię w nawiasach: (string | number)[] albo Array<string | number>, gdzie każdy element może mieć którykolwiek z typów. string[] | number[] to coś innego: cała tablica to same stringi albo same liczby. Bez nawiasów string | number[] oznacza string albo tablicę liczb.

Czym różni się unia od przecięcia typów?

Unia A | B to wartość, która ma jeden z typów, więc możesz używać tylko tego, co wspólne. Przecięcie A & B to wartość, która jest oboma naraz, więc ma wszystkie składowe obu. Dla typów obiektów A | B przyjmuje więcej wartości, a A & B wymaga więcej właściwości.

Jak sprawdzić, jakiego typu jest wartość unii?

Użyj sprawdzenia w czasie wykonania, które TypeScript rozumie: typeof x === "string" dla typów prostych, Array.isArray(x) dla tablic, x instanceof Date dla klas, "prop" in x dla kształtów obiektów albo x.kind === "circle", gdy elementy mają wspólny znacznik z literałem. Do własnej logiki napisz funkcję type guard zwracającą x is T.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ