Проверка полноты с помощью never
Выявляйте необработанные случаи во время компиляции с помощью never
«Проверка полноты с помощью never» — бесплатный урок TypeScript Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения TypeScript Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс TypeScript Academy содержит 4 уроков всего.
Проблема полноты обработки
Когда Вы добавляете новый элемент объединения, легко забыть обработать его где-нибудь ещё. Проверка полноты обработки превращает такую оплошность в ошибку компиляции.
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; side: number };
// If we add "triangle" later, we want every switch to complain.Тип never
Тип never представляет значения, которые никогда не могут возникнуть. Если обработаны все варианты, значение, попавшее в ветвь по умолчанию, имеет тип never.
function fail(): never {
throw new Error("unreachable");
}
// never is assignable to nothing except never itself.Присваивание never в ветви по умолчанию
В ветви default присвойте значение переменной типа never. Если обработаны все варианты, присваивание скомпилируется; если нет — произойдёт ошибка.
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; side: number };
function area(s: Shape): number {
switch (s.kind) {
case "circle": return Math.PI * s.radius ** 2;
case "square": return s.side ** 2;
default:
const _exhaustive: never = s;
return _exhaustive;
}
}
console.log(area({ kind: "square", side: 3 }));Что происходит, если забыть вариант
Если Вы добавите элемент triangle, но забудете его вариант, s в ветви по умолчанию больше не будет иметь тип never, поэтому присваивание завершится ошибкой во время компиляции.
// type Shape = ... | { kind: "triangle"; base: number; height: number };
// Now in default, s is { kind: "triangle"; ... }
// const _exhaustive: never = s; // Error: triangle not assignable to neverВспомогательная функция assertNever
Повторно используемая функция assertNever выносит этот шаблон в одно место. Она принимает never и выбрасывает ошибку, показывая, что ветвь недостижима.
function assertNever(value: never): never {
throw new Error("Unhandled case: " + JSON.stringify(value));
}
console.log(typeof assertNever);Использование assertNever в switch
Вызовите assertNever(s) в ветви по умолчанию. Он обеспечивает полноту обработки во время компиляции и выдаёт понятную ошибку во время выполнения, если до этой ветви всё же дойдёт выполнение.
function assertNever(value: never): never {
throw new Error("Unhandled: " + JSON.stringify(value));
}
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; side: number };
function area(s: Shape): number {
switch (s.kind) {
case "circle": return Math.PI * s.radius ** 2;
case "square": return s.side ** 2;
default: return assertNever(s);
}
}
console.log(area({ kind: "circle", radius: 1 }).toFixed(2));Безопасность во время компиляции и выполнения
Проверка с помощью never обнаруживает пропущенные варианты до запуска кода, а выброшенная ошибка защищает Вас, если что-то всё же прошло проверку типов и возникло во время выполнения.
function assertNever(x: never): never {
throw new Error("Unhandled: " + String(x));
}
// Compile error if a case is missing; runtime throw as a backstop.
console.log("two layers of safety");Полнота обработки без default
Если функция имеет явно заданный тип возвращаемого значения, а switch охватывает каждый вариант, TypeScript также может сообщить о пропущенном возврате — это ещё одна форма проверки полноты.
type Light = "red" | "yellow" | "green";
function next(l: Light): Light {
switch (l) {
case "red": return "green";
case "yellow": return "red";
case "green": return "yellow";
}
// No default needed; all cases return.
}
console.log(next("red"));Полнота обработки в цепочках if/else
Тот же подход работает с if/else. После обработки каждого варианта последняя ветвь else получает значение типа never.
function assertNever(x: never): never { throw new Error("bad"); }
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; side: number };
function name(s: Shape): string {
if (s.kind === "circle") return "circle";
else if (s.kind === "square") return "square";
else return assertNever(s);
}
console.log(name({ kind: "square", side: 2 }));Почему never — правильный инструмент
Поскольку значение типа never нельзя присвоить никакому другому типу, любой оставшийся вариант нарушает присваивание. Поэтому never идеально подходит для обнаружения необработанных вариантов.
// Only never is assignable to never.
let x: never;
// x = "hi"; // Error
// Any concrete leftover type fails the same way.
console.log("never catches gaps");Полнота обработки как страховка при рефакторинге
Если использовать assertNever повсюду, добавление элемента объединения создаст аккуратный список ошибок компиляции, указывающих точно на каждое место, которое нужно обновить.
function assertNever(x: never): never { throw new Error("unhandled"); }
type Status = "idle" | "busy";
function render(s: Status): string {
switch (s) {
case "idle": return "Idle";
case "busy": return "Busy";
default: return assertNever(s);
}
}
console.log(render("idle"));Быстрая проверка: полнота обработки
Проверьте, насколько хорошо Вы поняли проверку полноты обработки.
Итоги: полнота обработки с never
Вы научились присваивать значение по умолчанию переменной типа never (или передавать его в assertNever), чтобы компилятор заставлял Вас обрабатывать каждый вариант. Благодаря этому забытые варианты превращаются в ошибки компиляции.
function assertNever(x: never): never { throw new Error("unhandled"); }
type Shape = { kind: "circle"; radius: number } | { kind: "square"; side: number };
function f(s: Shape) {
switch (s.kind) {
case "circle": return s.radius;
case "square": return s.side;
default: return assertNever(s);
}
}
console.log(f({ kind: "circle", radius: 5 }));Часто задаваемые вопросы
Урок «Проверка полноты с помощью never» бесплатный?
Да — полный текст урока «Проверка полноты с помощью never» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс TypeScript Academy, подпишись на CoddyKit PRO. Курс TypeScript Academy содержит 4 уроков всего.
Чему я научусь в уроке «Проверка полноты с помощью never»?
Выявляйте необработанные случаи во время компиляции с помощью never Ты практикуешь TypeScript Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать TypeScript Academy?
Предыдущий опыт не требуется. TypeScript Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.
Сколько времени занимает урок «Проверка полноты с помощью never»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке TypeScript Academy?
Да. Каждый урок TypeScript Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Создание дискриминируемых объединений
- Сужение по дискриминатору
- Проверка полноты с помощью never
- Моделирование конечных автоматов