Оператор switch в TypeScript это switch из JavaScript с проверкой типов. Он сравнивает значение с каждым case через ===, выполняет совпавшую ветку и останавливается на break или return. TypeScript сужает значение внутри каждого case и умеет проверять, что обработано каждое возможное значение.
Метки подряд (case "sat": case "sun":) разделяют одну ветку. return завершает функцию, поэтому break после него не нужен.
Синтаксис
switch (expression) {
case value1:
// runs when expression === value1
break;
case value2:
case value3:
// runs for value2 or value3
break;
default:
// runs when nothing else matched
}
- Сравнение использует строгое равенство,
===:case 1не совпадёт со строкой"1". - Без
break,returnилиthrowвыполнение продолжается в следующем case (проваливание). defaultнеобязателен и может стоять где угодно, хотя обычно его ставят последним.- Значение
case, про которое TypeScript может доказать, что оно никогда не совпадёт, даёт ошибку TS2678. Для параметра с типом"a" | "b"строкаcase "c":выдаётType '"c"' is not comparable to type '"a" | "b"'., и это ловит опечатки в метках case.
Сужение внутри каждого case
В каждом case TypeScript знает, какое значение совпало, и сужает тип. Особенно полезно это с размеченным объединением: делайте switch по общему свойству-метке, и каждый case увидит соответствующий вариант с его собственными свойствами.
shape.radius компилируется только в case "circle". За его пределами shape может оказаться прямоугольником, у которого нет radius. default отсутствует, а функция всё равно проходит проверку как возвращающая number, потому что TypeScript видит, что три case покрывают каждое значение kind. Другие приёмы на этой основе описаны на странице размеченные объединения.
Исчерпывающий switch через never
Функция выше перестанет компилироваться, если добавить четвёртую фигуру без case (TS2366, пропущенный return). Но это работает, только когда функция возвращает значение. Чтобы гарантия работала и в void-коде и давала более понятное сообщение, добавьте default, который присваивает значение типу never:
index.ts(14,19): error TS2322: Type '{ kind: "triangle"; base: number; height: number; }' is not assignable to type 'never'.
После обработанных case для shape остаётся только вариант треугольника, а его нельзя присвоить never. Ошибка называет ровно то, что пропущено. Добавьте case "triangle": return (shape.base * shape.height) / 2;, и default увидит never, так что блок скомпилируется и напечатает 9. throw по-прежнему защищает от плохих данных во время выполнения, например от kind из JSON, которого типы не предусмотрели.
Во многих кодовых базах проверку оборачивают во вспомогательную функцию:
Тот же приём работает для enum: делайте switch по значению enum и передавайте его в assertNever в default.
switch (true) для диапазонов и условий
switch сравнивает значения, поэтому для диапазонов нужен трюк: switch по true и условие в каждом case. Начиная с TypeScript 5.3 такие условия сужают типы так же, как операторы if.
Case проверяются по порядку, поэтому самые конкретные ставьте первыми. Читается ли это лучше, чем if / else if, дело вкуса; поведение одинаковое.
Проваливание и break
Case с кодом, но без break, return или throw проваливается в следующий case. Почти всегда это ошибка. Опция компилятора noFallthroughCasesInSwitch (не входит в strict) превращает это в ошибку TS7029, Fallthrough case in switch., но по-прежнему разрешает пустые метки подряд.
Такое намеренное проваливание работает, но с включённой noFallthroughCasesInSwitch его придётся переписать, например отдельными проверками if. Обычно так и понятнее.
Переменные внутри case
Всё тело switch это один блок, поэтому const, объявленная в одном case, видна в остальных (а повторное объявление даёт ошибку). Оберните case в фигурные скобки, чтобы дать ему собственную область видимости:
Без скобок второе const unit даёт ошибку компиляции TS2451, Cannot redeclare block-scoped variable 'unit'.
switch или поиск по объекту
Когда каждый case просто отображает одно значение в другое, объект с типом Record короче, и TypeScript проверяет, что присутствует каждый ключ:
Пропуск ключа даёт ошибку компиляции, то есть ту же полноту, что и проверка через never. Оставьте switch для случаев, где выполняется разная логика, сужаются варианты объединения или нужен ранний возврат.
Часто задаваемые вопросы
Как написать оператор switch в TypeScript?
Точно так же, как в JavaScript: switch (value) { case "a": ...; break; default: ... }. Значения case сравниваются через ===. TypeScript добавляет проверки: значение case, которое никогда не совпадёт с типом выражения, даёт ошибку (TS2678), а внутри каждого case переменная сужается до этого случая.
Как сделать switch исчерпывающим в TypeScript?
Добавьте default, который присваивает значение переменной типа never: default: { const unreachable: never = value; throw new Error(...) }. Если обработаны все члены объединения, значение там имеет тип never, и код компилируется. Если какой-то пропущен, компилятор назовёт пропущенный член (TS2322).
Как обработать несколько case одним кодом в switch TypeScript?
Поставьте метки подряд без кода между ними: case "sat": case "sun": return "weekend";. Пустые case проваливаются в следующий. Опция noFallthroughCasesInSwitch сообщает только о case, в которых есть код и нет break или return, так что метки подряд остаются разрешены.
Сужает ли switch (true) типы в TypeScript?
Да, начиная с TypeScript 5.3. В switch (true) { case typeof x === "string": ... } внутри этого case x сужается до string, так же как в if. Это читаемая альтернатива цепочке if/else if с проверками диапазонов или типов.