Skip to main content

Exhaustive check через never

Коротка відповідь: зроби дискримінований union і додай у switch "сторож" на основі типу never. Тоді при додаванні нового варіанта TypeScript видасть помилку компіляції.


Базовий прийом (exhaustive check через never)

javascript
type Shape = | { kind: "circle"; radius: number } | { kind: "square"; side: number } | { kind: "triangle"; base: number; height: number }; function area(s: Shape): number { switch (s.kind) { case "circle": return Math.PI * s.radius ** 2; case "square": return s.side ** 2; case "triangle": return (s.base * s.height) / 2; default: // якщо з'явиться новий варіант, тут s стане НЕ 'never' → помилка const _exhaustive: never = s; throw new Error(`Unhandled case: ${_exhaustive}`); } }
  • Коли ти додаси, наприклад, { kind: "rectangle" }, s у default перестане бути neverпомилка компіляції змусить дописати case.

Варіант із допоміжною функцією assertNever

Зручно, якщо ти часто робиш такі перевірки:

javascript
function assertNever(x: never): never { throw new Error(`Unhandled case: ${String(x)}`); } function area(s: Shape): number { switch (s.kind) { case "circle": return Math.PI * s.radius ** 2; case "square": return s.side ** 2; case "triangle": return (s.base * s.height) / 2; default: return assertNever(s); // тут спрацює помилка типів при новому варіанті } }

Без default: "післясвітчевий" сторож

Якщо не хочеш default, можна вимагати, щоб усі гілки повертали значення, а після switch - перевірити never:

javascript
function area(s: Shape): number { switch (s.kind) { case "circle": return Math.PI * s.radius ** 2; case "square": return s.side ** 2; case "triangle": return (s.base * s.height) / 2; } const _exhaustive: never = s; // спрацює при додаванні нового kind return _exhaustive; }

Корисні налаштування та лінтинг

  • ESLint-правило: @typescript-eslint/switch-exhaustiveness-check - підсвічує незакриті switch.
  • Прапорець компілятора noFallthroughCasesInSwitch допомагає уникати "протікань" між case (не про вичерпність, але корисно).

Підсумок

Щоб гарантувати, що switch обробив усі варіанти union:

  1. Використовуй дискриміноване поле (наприклад, kind).
  2. Додай exhaustive check через присвоєння до neverdefault або після switch).
  3. (Опційно) Винеси перевірку в assertNever.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.