Можно ли использовать Never в Switch Case?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Можно ли использовать тип Never в конструкции Switch Case в TypeScript?
Да, тип Never можно и нужно использовать в switch-case, особенно в TypeScript, для реализации исчерпывающей проверки (exhaustiveness checking). Это мощный паттерн, гарантирующий безопасность кода во время компиляции.
Что такое тип Never?
Never — это специальный тип в TypeScript, представляющий значения, которые никогда не возникают. Он используется в ситуациях, которые никогда не должны происходить:
- Возвращаемый тип функции, которая всегда выбрасывает исключение (
throw new Error()). - Возвращаемый тип функции с бесконечным циклом.
- Как тип переменной в ветке кода, которая недостижима.
Паттерн Exhaustiveness Checking с Never
Ключевое применение в switch — проверка, что обработаны все возможные варианты типа (обычно union type). Если мы добавим новый вариант в тип, но забудем добавить обработку в switch, TypeScript сообщит об ошибке компиляции.
Рассмотрим пример:
type Shape = 'circle' | 'square' | 'triangle';
function getArea(shape: Shape): number {
switch (shape) {
case 'circle':
return Math.PI * 10 ** 2;
case 'square':
return 20 * 20;
case 'triangle':
return 0.5 * 10 * 15;
default:
// Здесь TypeScript проверяет исчерпываемость!
const _exhaustiveCheck: never = shape;
throw new Error(`Неизвестная форма: ${shape}`);
}
}
Как это работает:
- В
defaultветке мы пытаемся присвоить переменной_exhaustiveCheckтипаneverзначениеshape. - Пока
switchобрабатывает все варианты типаShape, код вdefaultветке недостижим, иshapeв этой точке имеет типnever. Присваиваниеnever = neverдопустимо. - Добавим новый вариант в тип, но забудем про
switch:
type Shape = 'circle' | 'square' | 'triangle' | 'hexagon'; // Добавили 'hexagon'
function getArea(shape: Shape): number {
switch (shape) {
case 'circle':
return Math.PI * 10 ** 2;
case 'square':
return 20 * 20;
case 'triangle':
return 0.5 * 10 * 15;
default:
// ОШИБКА КОМПИЛЯЦИИ!
// Тип '"hexagon"' не может быть присвоен типу 'never'.
const _exhaustiveCheck: never = shape;
throw new Error(`Неизвестная форма: ${shape}`);
}
}
Вариации и лучшие практики
- Использование
assertNever: Часто проверку выносят в отдельную функцию для удобства повторного использования.
function assertNever(x: never): never {
throw new Error(`Достигнут необрабатываемый вариант: ${x}`);
}
type Shape = 'circle' | 'square';
function getArea(shape: Shape): number {
switch (shape) {
case 'circle':
return Math.PI * 10 ** 2;
case 'square':
return 20 * 20;
default:
// Если мы добавим в тип новый вариант, например 'triangle',
// то эта строка вызовет ошибку компиляции,
// так как shape уже не будет типом never.
return assertNever(shape);
}
}
- Применение с Discriminated Unions: Паттерн особенно эффективен с различающимися (tagged) объединениями, где каждое значение имеет общее поле-метку (часто
typeилиkind).
type Success = { type: 'success'; data: string };
type Error = { type: 'error'; message: string };
type Loading = { type: 'loading' };
type ApiState = Success | Error | Loading;
function handleState(state: ApiState) {
switch (state.type) {
case 'success':
console.log(state.data);
break;
case 'error':
console.error(state.message);
break;
case 'loading':
console.log('Loading...');
break;
default:
const _exhaustiveCheck: never = state;
return _exhaustiveCheck;
}
}
Итог
Использование never в switch-case — это не просто возможность, а один из лучших паттернов TypeScript для повышения надежности кода. Он превращает потенциальные ошибки времени выполнения (когда появляется непредусмотренное значение) в ошибки компиляции, которые легко обнаружить и исправить на этапе разработки. Это яркий пример того, как статическая типизация помогает писать более безопасный и поддерживаемый код, особенно в больших кодовых базах.