JS Tower
JavaScript & TypeScriptМодуль 8: Продвинутый TypeScript

Union и Intersection

Объединение и пересечение типов

Цель урока

В этом уроке ты научишься:

  • Создавать union типы
  • Использовать intersection типы
  • Применять discriminated unions

Union Types (|)

Значение может быть одним из нескольких типов:

let id: string | number;

id = "abc";  // OK
id = 123;    // OK
// id = true; // Ошибка!

function printId(id: string | number) {
  console.log("ID: " + id);
}

Сужение типа

function printId(id: string | number) {
  if (typeof id === "string") {
    console.log(id.toUpperCase()); // string
  } else {
    console.log(id.toFixed(2));    // number
  }
}

Intersection Types (&)

Объединяет несколько типов в один:

type Person = {
  name: string;
  age: number;
};

type Employee = {
  company: string;
  position: string;
};

type Worker = Person & Employee;

let worker: Worker = {
  name: "Иван",
  age: 25,
  company: "Tech Corp",
  position: "Разработчик"
};

Discriminated Unions

Union с общим полем-дискриминатором:

type Circle = {
  kind: "circle";
  radius: number;
};

type Rectangle = {
  kind: "rectangle";
  width: number;
  height: number;
};

type Shape = Circle | Rectangle;

function getArea(shape: Shape): number {
  switch (shape.kind) {
    case "circle":
      return Math.PI * shape.radius ** 2;
    case "rectangle":
      return shape.width * shape.height;
  }
}

let circle: Circle = { kind: "circle", radius: 5 };
console.log(getArea(circle)); // 78.54...

Exhaustive Check

Проверка, что обработаны все варианты:

type Shape = Circle | Rectangle | Triangle;

function getArea(shape: Shape): number {
  switch (shape.kind) {
    case "circle":
      return Math.PI * shape.radius ** 2;
    case "rectangle":
      return shape.width * shape.height;
    default:
      // Если забыли Triangle, будет ошибка
      const _exhaustive: never = shape;
      return _exhaustive;
  }
}

Практические примеры

Результат операции

type Success<T> = {
  success: true;
  data: T;
};

type Failure = {
  success: false;
  error: string;
};

type Result<T> = Success<T> | Failure;

function fetchUser(id: number): Result<{ name: string }> {
  if (id > 0) {
    return { success: true, data: { name: "Иван" } };
  }
  return { success: false, error: "Неверный ID" };
}

let result = fetchUser(1);
if (result.success) {
  console.log(result.data.name); // TypeScript знает, что data есть
} else {
  console.log(result.error);
}

Практика

Задание 1: Union

Задача: Создай функцию для форматирования значения.

Запустите код для проверки
Loading...
Ваш вывод:
Ожидаемый результат:
HELLO
3.14

Задание 2: Intersection

Задача: Объедини два типа.

Запустите код для проверки
Loading...
Ваш вывод:
Ожидаемый результат:
Иван 25

Задание 3: Discriminated Union

Задача: Создай union для событий.

Запустите код для проверки
Loading...
Ваш вывод:
Ожидаемый результат:
Клик по кнопке
Нажата клавиша Enter

Проверь себя

  1. Чем union отличается от intersection?
  2. Что такое discriminated union?
  3. Зачем нужен exhaustive check?