При работе с объединениями типов (Discriminated Unions) важно убедиться, что мы обработали все возможные варианты. Это называется исчерпывающей проверкой (Exhaustiveness checking).
Для этого в TypeScript используется тип never. В конструкции switch мы можем добавить ветку default, в которой присвоим непроверенное значение переменной типа never. Если мы забыли обработать какой-то вариант (case), TypeScript увидит, что в default попадает что-то отличное от never, и подсветит ошибку.
Эта техника позволяет защитить код от багов при расширении системы типов: добавили новый статус заказа — компилятор сразу напомнит, где его нужно обработать.
- Модуль 1: Введение в TypeScript
- Модуль 2: Примитивные типы
- Модуль 3: Специальные типы
- Модуль 4: Массивы
- Модуль 5: Кортежи (Tuples)
- Модуль 6: Объекты
- Модуль 7: Функции
- Модуль 8: Union типы
- Модуль 9: Литеральные типы
- Модуль 10: Type Aliases
- Модуль 11: Интерфейсы
- Модуль 12: Type Guards и Narrowing
- Модуль 13: Enums
- Модуль 14: Классы
- Модуль 15: Generics — основы
- Модуль 16: Generics — ограничения
- Модуль 17: Utility Types — базовые
- Модуль 18: Utility Types — работа с Union
- Модуль 19: Utility Types — функции
- Модуль 20: Type Assertions
- Модуль 21: Keyof и Typeof операторы
- Модуль 22: Mapped Types
- Модуль 23: Conditional Types
- Модуль 24: Discriminated Unions
- Паттерн Discriminated Union.
- Exhaustiveness checking.
- Switch с Discriminated Unions.
- Модуль 25: Модули и типы
- Модуль 26: Declaration Files
- Модуль 27: Типизация асинхронного кода
- Модуль 28: Практические паттерны
1. Присвоение never
В этом задании вам нужно дополнить фрагмент кода на TypeScript, реализующий исчерпывающую проверку (exhaustiveness checking) для размеченного объединения (discriminated union). В блоке `default` конструкции `switch` необходимо присвоить необработанное значение переменной типа `never`, чтобы TypeScript мог проверить, что все возможные варианты объединения обработаны. Заполните пропуск именем аргумента функции, чтобы код стал синтаксически корректным и типобезопасным.
type Circle = { kind: 'circle'; radius: number };
type Square = { kind: 'square'; side: number };
type Shape = Circle | Square;
function getArea(shape: Shape): number {
switch (shape.kind) {
case 'circle':
return Math.PI * shape.radius ** 2;
case 'square':
return shape.side ** 2;
default:
const exhaustiveCheck: never = input1S;
return exhaustiveCheck;
}
}2. Логика проверки
Проанализируйте TypeScript-код сверху и восстановите последовательность логических шагов выполнения программы. Шаги снизу перемешаны — расставьте их в правильном порядке, чтобы отразить, как программа обрабатывает входную фигуру и выполняет проверку на полноту обработки всех вариантов.
type Circle = { kind: 'circle'; radius: number };
type Square = { kind: 'square'; side: number };
type Shape = Circle | Square;
function processShape(shape: Shape): number {
switch (shape.kind) {
case 'circle':
return Math.PI * shape.radius * shape.radius;
case 'square':
return shape.side * shape.side;
default:
const _exhaustiveCheck: never = shape;
throw new Error(`Unknown shape: ${_exhaustiveCheck}`);
}
}
// Входные данные
const myShape: Shape = { kind: 'circle', radius: 5 };
const area = processShape(myShape);
console.log(area);3. Необработанный вариант
В этом фрагменте TypeScript-кода используется discriminated union для геометрических фигур. В union был добавлен новый тип 'Triangle', но в switch-операторе отсутствует соответствующий case для его обработки. Из-за этого возникает ошибка TypeScript в строке с exhaustive check. Найдите и исправьте строку, чтобы добавить обработку треугольника и обеспечить полноту проверки всех вариантов union.
type Circle = { kind: 'Circle'; radius: number };type Square = { kind: 'Square'; sideLength: number };type Triangle = { kind: 'Triangle'; base: number; height: number };type Shape = Circle | Square | Triangle; function getArea(shape: Shape): string { switch (shape.kind) { case 'Circle': return `Circle area: ${Math.PI * shape.radius ** 2}`; case 'Square': return `Square area: ${shape.sideLength ** 2}`; // case 'Triangle': return `Triangle area: ${0.5 * shape.base * shape.height}`; default: const _exhaustiveCheck: never = shape; return _exhaustiveCheck; }}4. Поиск дискриминанта
В этом задании вам нужно разметить код TypeScript, используя discriminated unions. Найдите и отметьте дискриминантное свойство (общее свойство для различения типов) и его значения (конкретные значения, указывающие на тип). Доступные типы: 'Дискриминантное свойство' и 'Значение дискриминанта'.
interface Circle {
{{kind~|~t1}}: '{{circle~|~t2}}';
radius: number;
}
interface Square {
{{kind~|~t3}}: '{{square~|~t4}}';
sideLength: number;
}
type Shape = Circle | Square;
function getArea(shape: Shape): number {
switch (shape.kind) {
case 'circle':
return Math.PI * shape.radius ** 2;
case 'square':
return shape.sideLength ** 2;
}
}5. Тип ловушки
В этом задании вам нужно выбрать правильный тип TypeScript для переменной, используемой в проверке исчерпываемости (exhaustiveness checking) дискриминированных объединений (discriminated unions). Код содержит пропуск, куда следует подставить тип из выпадающего списка, чтобы TypeScript мог статически проверить, что все возможные случаи обработаны. Вы увидите фрагмент кода с оператором switch, и в ветке default необходимо указать тип, который гарантирует исчерпывающую проверку.
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; side: number };
function getArea(shape: Shape): number {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "square":
return shape.side ** 2;
default:
const exhaustiveCheck: input1S = shape;
throw new Error(`Unhandled shape: ${exhaustiveCheck}`);
}
}6. Блок защиты
Из предложенных строк соберите default ветку оператора switch, которая реализует проверку на полноту обработки (exhaustiveness checking) для discriminated union в TypeScript. Ветка default должна присваивать значение переменной типа never и возвращать её, чтобы компилятор гарантировал обработку всех вариантов. Одна из строк лишняя и не должна входить в решение.
default: const _exhaustiveCheck: never = val; return _exhaustiveCheck;} break;7. Результат при расширении
Проанализируйте приведённый код на TypeScript. Тип Shape был расширен новым вариантом Triangle, но функция getArea не была обновлена. Что произойдёт при попытке компиляции этого кода? Обратите внимание, что функция getArea объявлена как возвращающая число, и в ней используется switch по свойству kind.
type Shape = Circle | Square | Triangle;
interface Circle {
kind: 'circle';
radius: number;
}
interface Square {
kind: 'square';
sideLength: number;
}
interface Triangle {
kind: 'triangle';
base: number;
height: number;
}
function getArea(shape: Shape): number {
switch (shape.kind) {
case 'circle':
return Math.PI * shape.radius ** 2;
case 'square':
return shape.sideLength ** 2;
}
}