Сужение по дискриминатору
Позвольте TypeScript сужать варианты в операторах switch
«Сужение по дискриминатору» — бесплатный урок TypeScript Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения TypeScript Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс TypeScript Academy содержит 4 уроков всего.
Сужение по дискриминанту
После проверки дискриминанта TypeScript сужает объединение до подходящего элемента и открывает доступ к его специфическим полям. В этом и состоит главное преимущество такого подхода.
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; side: number };
function area(s: Shape) {
if (s.kind === "circle") return Math.PI * s.radius ** 2;
return s.side ** 2;
}
console.log(area({ kind: "circle", radius: 2 }).toFixed(2));Переключение по дискриминанту
Конструкция switch по дискриминанту — самый понятный способ обработать каждый вариант. Внутри каждого case тип сужается автоматически.
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;
}
}
console.log(area({ kind: "square", side: 5 }));Поля конкретного элемента в каждом варианте
В варианте "circle" доступно поле s.radius, а s.side недоступно. TypeScript точно знает, с каким элементом Вы работаете.
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; side: number };
function describe(s: Shape) {
switch (s.kind) {
case "circle": return "r=" + s.radius;
case "square": return "side=" + s.side;
}
}
console.log(describe({ kind: "circle", radius: 9 }));Сужение с помощью if
Вам не обязательно использовать switch. Простое сравнение дискриминанта в конструкции if выполняет сужение столь же эффективно.
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; side: number };
function perimeter(s: Shape): number {
if (s.kind === "circle") {
return 2 * Math.PI * s.radius;
}
return 4 * s.side;
}
console.log(perimeter({ kind: "square", side: 3 }));Обращение к неправильному полю приводит к ошибке
До сужения нельзя обращаться к полю, специфичному для отдельного элемента. TypeScript сообщает об ошибке, поскольку это свойство существует не у каждого элемента.
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; side: number };
function broken(s: Shape) {
// Error: radius does not exist on Square
// return s.radius;
return s.kind;
}
console.log(broken({ kind: "square", side: 1 }));Сужение с логическими дискриминантами
Для объединения { ok: true } | { ok: false } проверка if (r.ok) сужает тип до элемента, соответствующего успешному результату.
type Result =
| { ok: true; data: string }
| { ok: false; error: string };
function handle(r: Result): string {
if (r.ok) return "Data: " + r.data;
return "Error: " + r.error;
}
console.log(handle({ ok: false, error: "boom" }));Сужение с досрочным возвратом
Досрочный возврат для одного варианта сужает оставшуюся часть кода до других вариантов. Это удобный способ последовательно исключать варианты.
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; side: number };
function label(s: Shape): string {
if (s.kind === "circle") return "circle";
// s is now narrowed to Square here
return "square with side " + s.side;
}
console.log(label({ kind: "square", side: 8 }));Комбинирование с другими условиями
Проверку дискриминанта можно сочетать с другой логикой. TypeScript сохраняет сужение, пока условие дискриминанта остаётся истинным в этой ветви.
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; side: number };
function bigCircle(s: Shape): boolean {
return s.kind === "circle" && s.radius > 100;
}
console.log(bigCircle({ kind: "circle", radius: 150 }));Сужение в фильтрах массивов
Сужение работает и внутри обратных вызовов. Здесь каждая фигура проверяется отдельно по мере перебора.
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; side: number };
const shapes: Shape[] = [
{ kind: "circle", radius: 1 },
{ kind: "square", side: 2 }
];
shapes.forEach(s => {
if (s.kind === "circle") console.log("c", s.radius);
else console.log("s", s.side);
});Возврат из switch сохраняет безопасность типов
Если конструкция switch обрабатывает каждый вариант и возвращает результат, TypeScript выводит точный тип результата на основе объединения результатов всех вариантов.
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; side: number };
function tag(s: Shape) {
switch (s.kind) {
case "circle": return s.radius;
case "square": return s.side;
}
}
console.log(tag({ kind: "circle", radius: 4 }));Объединяем приёмы сужения
Используете ли Вы switch или if, именно проверка дискриминанта делает обращение к полям элемента безопасным. Далее мы обеспечим обработку каждого варианта.
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; side: number };
function area(s: Shape): number {
return s.kind === "circle" ? Math.PI * s.radius ** 2 : s.side ** 2;
}
console.log(Math.round(area({ kind: "circle", radius: 10 })));Быстрая проверка: сужение
Проверьте, насколько хорошо Вы поняли сужение по дискриминанту.
Итоги: сужение по дискриминанту
Вы увидели, что проверка дискриминанта с помощью switch или if сужает объединение до одного элемента, открывая доступ только к полям этого элемента. Досрочные возвраты и составные условия также сохраняют сужение.
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; side: number };
const s: Shape = { kind: "square", side: 7 };
console.log(s.kind === "square" ? s.side : 0);Часто задаваемые вопросы
Урок «Сужение по дискриминатору» бесплатный?
Да — полный текст урока «Сужение по дискриминатору» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс TypeScript Academy, подпишись на CoddyKit PRO. Курс TypeScript Academy содержит 4 уроков всего.
Чему я научусь в уроке «Сужение по дискриминатору»?
Позвольте TypeScript сужать варианты в операторах switch Ты практикуешь TypeScript Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать TypeScript Academy?
Предыдущий опыт не требуется. TypeScript Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.
Сколько времени занимает урок «Сужение по дискриминатору»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке TypeScript Academy?
Да. Каждый урок TypeScript Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Создание дискриминируемых объединений
- Сужение по дискриминатору
- Проверка полноты с помощью never
- Моделирование конечных автоматов