Перейти до основного вмісту
Модуль 2 · Урок 2 із 24/14 у курсі~15 min
Модульні уроки (2/2)

Союзи і звуження

У реальному світі змінні та відповіді API не завжди мають єдиний фіксований тип. TypeScript пропонує Union Types для обробки змінності та Type Narrowing для безпечної роботи з ними під час виконання.


Типи спілок

Тип об’єднання дозволяє змінній приймати значення різних типів. Це виражається символом вертикальної риски (|):

TS
let result: number | string;
result = 42; // Valid
result = 'Error 404'; // Valid

Однак під час роботи з типом об’єднання ми не можемо напряму викликати методи, які належать лише до одного з типів (наприклад, ми не можемо викликати .toUpperCase(), якщо змінна також може бути number). Спочатку треба «звузити» тип.


Тип Звуження

Звуження типу — це процес, у якому TypeScript аналізує структури потоку керування (наприклад, if або switch), щоб визначити більш конкретний тип для змінної під час виконання.

Існує кілька способів виконання звуження:

1. Оператор typeof

Ідеально підходить для розрізнення примітивних типів:

TS
function printLength(value: string | number) {
  if (typeof value === 'string') {
    // TypeScript knows 'value' is a string here
    console.log(value.length);
  } else {
    // TypeScript knows 'value' is a number here
    console.log(value.toFixed(2));
  }
}

2. Оператор in

Використовується для перевірки наявності певної властивості об’єкта:

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

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

Дискриміновані профспілки

Шаблон Дискриміновані об’єднання складається зі створення об’єктів, які мають спільну властивість з унікальним літеральним значенням (званим дискримінатором). TypeScript розпізнає цей дискримінатор і автоматично звужує тип у межах умовних блоків.

TS
interface SuccessResponse {
  status: 'success'; // Literal discriminator
  data: string;
}

interface ErrorResponse {
  status: 'error'; // Literal discriminator
  errorMessage: string;
}

type ApiResponse = SuccessResponse | ErrorResponse;

function handleResponse(response: ApiResponse) {
  if (response.status === 'success') {
    console.log('Data received:', response.data);
  } else {
    console.error('An error occurred:', response.errorMessage);
  }
}

Спробуйте самі

Вправа 1: Типи союзів

вправи#ts.m2.l2.e1
Спроби: 0Завантаження…

Оголошіть змінну з іменем id, яка може бути або числом, або рядком. Спочатку ініціалізуйте його числом 101, а потім призначте значення рядка "USER-101".

Завантаження редактора…
Показати підказку

Використовуйте | оператор для об’єднання числа та рядка в оголошенні змінної let.

Рішення доступне після 3 спроб

Вправа 2: Звуження базового типу

вправи#ts.m2.l2.e2
Спроби: 0Завантаження…

Створіть функцію під назвою formatInput, яка приймає вхідний параметр типу string або number. Якщо введення є рядком, повертає введення, перетворене на верхній регістр. Якщо це число, поверніть вхідні дані, помножені на 2. Явно вкажіть типи.

Завантаження редактора…
Показати підказку

Використовуйте typeof input === 'string' всередині блоку if, щоб розгалужити поведінку.

Рішення доступне після 3 спроб

Вправа 3: Звуження за допомогою "в"

вправи#ts.m2.l2.e3
Спроби: 0Завантаження…

Маючи два інтерфейси Car (з методом drive) і Boat (з методом sail), напишіть функцію з назвою moveVehicle, яка приймає параметр vehicle типу Car або Boat. Якщо транспортний засіб має властивість drive, виконайте метод drive(). В іншому випадку виконайте метод sail().

Завантаження редактора…
Показати підказку

Використовуйте оператор in у формі 'drive' in vehicle, щоб звузити інтерфейс.

Рішення доступне після 3 спроб

Вправа 4: Дискримінована форма об’єднання

вправи#ts.m2.l2.e4
Спроби: 0Завантаження…

Визначте тип Shape, який є об’єднанням двох типів: Circle та Square. Circle має властивість типу 'circle' (літеральне значення) і радіус (число). Square має властивість типу 'квадрат' (літеральне значення) і сторону (число). Потім напишіть функцію getArea, яка приймає форму типу Shape і повертає площу як число (для кола Math.PI * радіус * радіус, для квадрата * сторона).

Завантаження редактора…
Показати підказку

Використовуйте shape.kind === 'circle' всередині getArea, щоб розрізнити тип і обчислити правильну площу.

Рішення доступне після 3 спроб