Przejdź do głównej treści
eLearner.app
Moduł 2 · Lekcja 2 z 24/14 w kursie~15 min
Lekcje modułu (2/2)

Unie i Zawężanie typów

W rzeczywistym świecie zmienne oraz odpowiedzi z interfejsów API nie zawsze mają jeden stały typ. TypeScript oferuje Typy Unii (Union Types) do zarządzania tą zmiennością oraz Zawężanie Typów (Type Narrowing) do bezpiecznej pracy z nimi w czasie wykonywania kodu.


Typy Unii (Union Types)

Typ unii pozwala zmiennej na akceptowanie wartości różnych typów. Zapisujemy go za pomocą pionowej kreski (|):

TS
let result: number | string;
result = 42; // Valido
result = 'Errore 404'; // Valido

Jednak gdy pracujemy z typem unii, nie możemy bezpośrednio wywoływać metod należących tylko do jednego z tych typów (na przykład nie możemy wywołać .toUpperCase(), jeśli zmienna może być również typu number). Najpierw musimy "zawęzić" typ.


Zawężanie Typów (Type Narrowing)

Co to jest Type Narrowing? To proces, w którym TypeScript analizuje struktury sterujące przepływem kodu (takie jak if czy switch), aby wywnioskować bardziej szczegółowy typ zmiennej w danym bloku kodu a runtime.

Istnieje kilka sposobów na zawężenie typu:

1. Operator typeof

Idealny do rozróżniania typów prymitywnych:

TS
function printLength(value: string | number) {
  if (typeof value === 'string') {
    // Qui TypeScript sa che 'value' è una stringa
    console.log(value.length);
  } else {
    // Qui TypeScript sa che 'value' è un numero
    console.log(value.toFixed(2));
  }
}

2. Operator in

Służy do sprawdzania, czy dany obiekt posiada określoną właściwość:

TS
interface Fish {
  swim: () => void;
}
interface Bird {
  fly: () => void;
}

function move(animal: Fish | Bird) {
  if ('swim' in animal) {
    animal.swim(); // Narrowing a Fish
  } else {
    animal.fly(); // Narrowing a Bird
  }
}

Unie Dyskryminowane (Discriminated Unions)

Wzorzec Unii Dyskryminowanych polega na tworzeniu obiektów współdzielących wspólną właściwość ze ściśle określoną wartością literalną (nazywaną dyskryminatorem). TypeScript rozpoznaje ten dyskryminator i automatycznie zawęża typ wewnątrz bloków warunkowych.

TS
interface SuccessResponse {
  status: 'success'; // Discriminatore letterale
  data: string;
}

interface ErrorResponse {
  status: 'error'; // Discriminatore letterale
  errorMessage: string;
}

type ApiResponse = SuccessResponse | ErrorResponse;

function handleResponse(response: ApiResponse) {
  if (response.status === 'success') {
    console.log('Dati ricevuti:', response.data);
  } else {
    console.error('Si è verificato un errore:', response.errorMessage);
  }
}

Spróbuj sam

Ćwiczenie 1: Typy Unii

Ćwiczenie#ts.m2.l2.e1
Próby: 0Ładowanie...

Zadeklaruj zmienną o nazwie id, która może być zarówno liczbą, jak i ciągiem znaków (string). Zainicjalizuj ją najpierw liczbą 101, a następnie przypisz wartość tekstową 'USER-101'.

Ładowanie edytora...
Pokaż wskazówkę

Użyj operatora | do połączenia typów number i string w deklaracji zmiennej let.

Rozwiązanie dostępne po 3 próbach

Ćwiczenie 2: Podstawy Type Narrowing

Ćwiczenie#ts.m2.l2.e2
Próby: 0Ładowanie...

Utwórz funkcję o nazwie formatInput, która przyjmuje parametr input typu string lub number. Jeśli input jest stringiem, zwróć go przekonwertowanego na wielkie litery. Jeśli jest liczbą, zwróć wartość pomnożoną przez 2. Określ typy jawnie.

Ładowanie edytora...
Pokaż wskazówkę

Użyj typeof input === 'string' wewnątrz bloku if, aby rozróżnić zachowanie.

Rozwiązanie dostępne po 3 próbach

Ćwiczenie 3: Narrowing z użyciem 'in'

Ćwiczenie#ts.m2.l2.e3
Próby: 0Ładowanie...

Mając dane dwa interfejsy: Car (z metodą drive) i Boat (z metodą sail), napisz funkcję o nazwie moveVehicle, która przyjmuje parametr vehicle typu Car lub Boat. Jeśli vehicle posiada właściwość drive, wywołaj metodę drive(). W przeciwnym razie wywołaj metodę sail().

Ładowanie edytora...
Pokaż wskazówkę

Użyj operatora in w postaci 'drive' in vehicle, aby zawęzić interfejs.

Rozwiązanie dostępne po 3 próbach

Ćwiczenie 4: Unia Dyskryminowana Shape

Ćwiczenie#ts.m2.l2.e4
Próby: 0Ładowanie...

Zdefiniuj typ Shape będący unią dwóch typów: Circle i Square. Circle ma właściwość kind ustawioną na 'circle' (wartość literalna) oraz radius (liczba). Square ma właściwość kind ustawioną na 'square' (wartość literalna) oraz side (liczba). Następnie napisz funkcję getArea, która przyjmuje parametr shape typu Shape i zwraca pole powierzchni jako liczbę (dla koła Math.PI * radius * radius, dla kwadratu side * side).

Ładowanie edytora...
Pokaż wskazówkę

Użyj shape.kind === 'circle' wewnątrz getArea, aby odróżnić typ i obliczyć właściwe pole.

Rozwiązanie dostępne po 3 próbach