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

Союзы и сужение

В реальном мире переменные и ответы API не всегда имеют один фиксированный тип. TypeScript предлагает Объединенные типы для обработки изменчивости и Сужение типов для безопасной работы с ними во время выполнения.


Типы союзов

Тип объединения позволяет переменной принимать значения разных типов. Это выражается с помощью символа вертикальной черты (|):

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, которая принимает входной параметр типа строки или числа. Если ввод является строкой, верните ввод, преобразованный в верхний регистр. Если это число, верните входные данные, умноженные на 2. Укажите типы явно.

Загрузка редактора…
Показать подсказку

Используйте typeof input === 'string' внутри блока if для разветвления поведения.

Решение доступно после 3 попыток

Упражнение 3: Сужение с помощью «in»

Упражнение#ts.m2.l2.e3
Попыток: 0Загрузка…

Учитывая два интерфейса Car (с методом Drive) и Boat (с методом Sail), напишите функцию с именем moveVehicle, которая принимает параметр транспортного средства типа Car или Boat. Если транспортное средство имеет свойство привода, выполните метод привода(). В противном случае выполните метод парус().

Загрузка редактора…
Показать подсказку

Используйте оператор in в форме «ездить» в автомобиле, чтобы сузить интерфейс.

Решение доступно после 3 попыток

Упражнение 4. Форма распознанного объединения

Упражнение#ts.m2.l2.e4
Попыток: 0Загрузка…

Определите тип Shape, который представляет собой объединение двух типов: Circle и Square. У круга есть свойство вида, для которого установлено значение «круг» (буквальное значение) и радиус (число). У Square есть свойство kind, для которого установлено значение «square» (буквальное значение) и сторона (число). Затем напишите функцию getArea, которая принимает форму типа Shape и возвращает площадь в виде числа (для круга Math.PI * радиус * радиус, для стороны квадрата * сторона).

Загрузка редактора…
Показать подсказку

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

Решение доступно после 3 попыток