0Pricing
TypeScript Academy · Урок

Создание дискриминируемых объединений

Помечайте элементы объединения общим свойством-дискриминатором

«Создание дискриминируемых объединений» — бесплатный урок TypeScript Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения TypeScript Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс TypeScript Academy содержит 4 уроков всего.

Что такое дискриминируемое объединение

Дискриминируемое объединение — это объединение объектных типов, у которых есть общее свойство literal, называемое дискриминантом. Этот общий признак позволяет TypeScript различать элементы объединения.

// The shared "kind" property is the discriminant
type Circle = { kind: "circle"; radius: number };
type Square = { kind: "square"; side: number };
type Shape = Circle | Square;

Свойство-дискриминант

Дискриминант должен быть литеральным типом (например, "circle"), а не широким типом вроде string. Каждый элемент получает собственное уникальное литеральное значение.

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

const c: Circle = { kind: "circle", radius: 10 };
console.log(c.kind); // "circle"

У каждого элемента свои поля

Помимо общего дискриминанта, каждый элемент содержит поля, имеющие смысл только для него. У круга есть radius, а у квадрата — side.

type Circle = { kind: "circle"; radius: number };
type Square = { kind: "square"; side: number };
type Rectangle = { kind: "rectangle"; width: number; height: number };
type Shape = Circle | Square | Rectangle;

Создание значений объединения

При создании значения TypeScript проверяет, что его структура в точности соответствует одному элементу объединения на основании свойства kind.

type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "square"; side: number };

const shapes: Shape[] = [
  { kind: "circle", radius: 5 },
  { kind: "square", side: 4 }
];
console.log(shapes.length); // 2

Почему не использовать необязательные поля

Свободный тип с необязательными полями (radius?, side?) допускает недопустимые сочетания. Дискриминируемое объединение не позволяет представить недопустимые состояния.

// Loose and error-prone: nothing stops radius + side together
type BadShape = { radius?: number; side?: number };
const bad: BadShape = { radius: 5, side: 4 }; // nonsense, but allowed

Имя дискриминанта выбираете Вы

Обычно признак называют kind или type, но подойдёт любое имя, если все элементы используют одно и то же имя свойства с различным литеральным значением.

type Event =
  | { type: "click"; x: number; y: number }
  | { type: "scroll"; delta: number };

const e: Event = { type: "click", x: 10, y: 20 };
console.log(e.type); // "click"

Моделирование вычислителя площади фигур

Дискриминируемые объединения особенно полезны, когда для каждого варианта требуется отдельная обработка. Здесь мы создаём фигуры, которые позже будет обрабатывать функция вычисления площади.

type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "square"; side: number };

const myShapes: Shape[] = [
  { kind: "circle", radius: 3 },
  { kind: "square", side: 6 }
];
console.log("Count:", myShapes.length);

Добавление третьего варианта

Объединения можно расширять. Добавление элемента triangle требует изменения всего в одну строку, а TypeScript учтёт его везде, где используется объединение.

type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "square"; side: number }
  | { kind: "triangle"; base: number; height: number };

const t: Shape = { kind: "triangle", base: 4, height: 8 };
console.log(t.kind);

Уникальные литералы предотвращают пересечения

Поскольку каждое значение kind уникально, неоднозначности нет. Значение является ровно одним элементом, а не двумя одновременно.

type A = { kind: "a"; value: number };
type B = { kind: "b"; label: string };
type Union = A | B;

function describe(u: Union) {
  return u.kind === "a" ? u.value : u.label;
}
console.log(describe({ kind: "b", label: "hi" }));

Дискриминанты с логическими признаками

Дискриминант не обязан быть строковым. Подойдёт и литерал типа boolean, что удобно для результатов успешных и неуспешных операций.

type Result =
  | { ok: true; data: string }
  | { ok: false; error: string };

const r: Result = { ok: true, data: "loaded" };
console.log(r.ok ? r.data : r.error);

Дискриминируемые объединения на практике

Теперь у Вас есть полноценная модель фигур. На следующем уроке мы выполним сужение по дискриминанту, чтобы безопасно обращаться к полю, специфичному для каждого элемента.

type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "square"; side: number };

const sample: Shape = { kind: "circle", radius: 7 };
console.log(sample.kind, "ready");

Быстрая проверка: дискриминанты

Проверьте, насколько хорошо Вы поняли дискриминируемые объединения.

Итоги: построение дискриминируемых объединений

Вы узнали, что дискриминируемое объединение объединяет объектные типы с общим литеральным дискриминантом, например kind. Каждый элемент содержит собственные поля, уникальные литералы предотвращают пересечения, а такой подход не позволяет представить недопустимые состояния.

type Shape =
  | { kind: "circle"; radius: number }
  | { kind: "square"; side: number };

const done: Shape = { kind: "square", side: 2 };
console.log("Recap complete:", done.kind);

Часто задаваемые вопросы

Урок «Создание дискриминируемых объединений» бесплатный?

Да — полный текст урока «Создание дискриминируемых объединений» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс TypeScript Academy, подпишись на CoddyKit PRO. Курс TypeScript Academy содержит 4 уроков всего.

Чему я научусь в уроке «Создание дискриминируемых объединений»?

Помечайте элементы объединения общим свойством-дискриминатором Ты практикуешь TypeScript Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать TypeScript Academy?

Предыдущий опыт не требуется. TypeScript Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.

Сколько времени занимает урок «Создание дискриминируемых объединений»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке TypeScript Academy?

Да. Каждый урок TypeScript Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Создание дискриминируемых объединений
  2. Сужение по дискриминатору
  3. Проверка полноты с помощью never
  4. Моделирование конечных автоматов
← Назад к TypeScript Academy