Создание дискриминируемых объединений
Помечайте элементы объединения общим свойством-дискриминатором
«Создание дискриминируемых объединений» — бесплатный урок 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 — локальная установка не требуется.
Все уроки этого курса
- Создание дискриминируемых объединений
- Сужение по дискриминатору
- Проверка полноты с помощью never
- Моделирование конечных автоматов