


Discriminated union в TypeScript объединяет несколько типов объектов с общим полем-признаком. Например, status: "success" означает, что у объекта есть data, а status: "error" связан с message. Проверка status позволяет компилятору выбрать нужный вариант и разрешить доступ только к его полям.
В разборе Mentoring.digital опишем состояние загрузки списка. Такой тип помогает согласовать ветви отображения, но сам не выполняет запросы и не проверяет пришедший с сервера JSON.
type LooseState = {
loading: boolean;
data?: string[];
error?: string;
};
Такой тип допускает одновременно loading, данные и ошибку. Иногда это осмысленная модель, например обновление уже показанных данных, но тогда нужно отдельно описать её правила. Для простого экрана с четырьмя взаимоисключающими состояниями набор необязательных полей позволяет слишком много непонятных комбинаций.
Удобнее связать обязательные данные с конкретным состоянием. Название status может быть другим, но значения признака должны различать варианты.
type LoadState<T> =
| { status: "idle" }
| { status: "loading" }
| { status: "success"; data: T }
| { status: "error"; message: string };
function label(state: LoadState<string[]>): string {
switch (state.status) {
case "idle": return "Ещё не загружено";
case "loading": return "Загрузка";
case "success": return `Найдено: ${state.data.length}`;
case "error": return state.message;
default: {
const unexpected: never = state;
return unexpected;
}
}
}
const ready: LoadState<string[]> = {status: "success", data: ["A"]};
console.log(label(ready));
В ветви success компилятор знает о data. В ветви error он знает о message. В idle и loading этих полей нет. Не приходится писать data! или принудительно приводить объект через as, чтобы скрыть неопределённость модели.
При настройке проекта включите strict. Если TypeScript уже установлен в проекте, файл можно проверить командой npx tsc --strict --noEmit state.ts. Команду выполняйте в проекте с установленным компилятором, чтобы npx не предлагал случайную загрузку другого пакета.
После обработки всех вариантов state в default получает тип never. Если добавить новый вариант, например {status: "cancelled"}, и забыть соответствующий case, присваивание переменной unexpected перестанет проходить проверку типов.
Это полезно при развитии приложения: ошибка указывает на обработчики, которые ещё не знают о новом состоянии. Не заменяйте проверку безусловным return "", если хотите сохранить этот сигнал. При этом проверка работает на этапе компиляции и не заменяет обработку неожиданных внешних данных во время исполнения.
{status: "success"} без data должно быть ошибкой типов.Union описывает допустимую форму отдельного состояния, но не разрешённый порядок переходов между состояниями. Если запрещено переходить из idle прямо в success, потребуется логика переходов. Если хотите показывать старые данные во время повторной загрузки, добавьте осмысленный вариант с нужными полями, а не разрушайте все ограничения optional-полями.
На курсе TypeScript с личным планом модель можно применить к реальному компоненту. Для консультации с ментором по TypeScript подготовьте перечень экранных состояний и переходов между ними: так обсуждение выйдет за рамки исправления одной ошибки компилятора.