Операторы и условные ветвления в TypeScript
Дальше: Циклы · Асинхронность · Типы и типизация · Справочник — объединения и пересечения
Операторы if, switch, ===, &&, ?? в TypeScript совпадают с JavaScript, но ветки часто сужают тип (type narrowing). Это связывает условия с системой типов и убирает целый класс ошибок доступа к полям.
Маршрут: переменные → ветвления → циклы → Асинхронность с LoadState.
База JS: условия и циклы. Discriminated unions — 10.md.
Сравнение и логика
const a = 5;
const b = "5";
console.log(a == b); // true — приведение типов
console.log(a === b); // false — предпочтительно в TS/JS
| Оператор | Назначение |
|---|---|
=== / !== | строгое сравнение без приведения |
&&, || | короткое замыкание; || осторожно с 0 и "" |
?? | значение по умолчанию только для null / undefined |
?. | optional chaining |
const port = config.port ?? 3000;
const label = input || "по умолчанию"; // 0 и "" подставят значение по умолчанию
Разбор:
- Для дефолтов конфигурации в strict-коде чаще
??, не||. ?.возвращаетundefined, если левая частьnull/undefined— тип результата учитывает это.
if и сужение типов (narrowing)
typeof для примитивов
function format(value: string | number): string {
if (typeof value === "string") {
return value.toUpperCase();
}
return value.toFixed(2);
}
Разбор:
- В ветке
stringкомпилятор знает типvalue. - После
ifвelseостаётсяnumber.
Проверка на null / undefined
function len(text: string | null): number {
if (text === null) {
return 0;
}
return text.length;
}
Truthiness — осторожно
function printCount(n: number | undefined): void {
if (n) {
// здесь n: number, но 0 отфильтрован!
console.log(n);
}
}
Разбор:
if (n)отсекает0— для чисел лучшеn !== undefined && n !== nullилиn != null.
Оператор in для объектов
type Cat = { kind: "cat"; meow: () => void };
type Dog = { kind: "dog"; bark: () => void };
type Pet = Cat | Dog;
function speak(pet: Pet): void {
if ("meow" in pet) {
pet.meow();
} else {
pet.bark();
}
}
Разбор:
inпроверяет наличие свойства в объекте и сужает union.- Хорошо сочетается с полем-дискриминантом
kind, но не заменяет его всегда.
Discriminated union и switch
type ApiResult =
| { status: "ok"; data: string }
| { status: "error"; code: number };
function handle(result: ApiResult): string {
switch (result.status) {
case "ok":
return result.data;
case "error":
return `Ошибка ${result.code}`;
default: {
const _exhaustive: never = result;
return _exhaustive;
}
}
}
Разбор:
- Поле
status— дискриминант: в каждой веткеcaseдоступны только релевантные поля. defaultсnever— exhaustiveness check: при добавлении варианта union компилятор укажет на неполныйswitch.
Тот же паттерн для UI — 10.md, 17.md.
if против switch
| Критерий | if / else if | switch |
|---|---|---|
| 2–3 ветки | удобно | избыточно |
| Много вариантов union | длинно | читаемо |
| Exhaustive check | вручную | default: never |
| Диапазоны чисел | удобно | возможно, но реже |
Тернарный оператор
const badge = user.role === "admin" ? "Админ" : "Пользователь";
const message =
state.status === "error" ? state.message : "OK";
Разбор:
- Тернарник выражение — можно присваивать и возвращать.
- Вложенные тернарники быстро теряют читаемость — переходите на
switchили функцию.
Сужение с Array.isArray и пользовательскими guard
function isStringArray(value: unknown): value is string[] {
return Array.isArray(value) && value.every((x) => typeof x === "string");
}
function process(raw: unknown): string[] {
if (!isStringArray(raw)) {
throw new Error("Expected string[]");
}
return raw.map((s) => s.trim());
}
Разбор:
value is string[]— type predicate: послеifтип сужен.- Предпочтительнее
as string[]для внешних данных — 6.md.
Пример parseInput
function parseInput(raw: unknown): number | null {
if (typeof raw === "number" && Number.isFinite(raw)) {
return raw;
}
if (typeof raw === "string" && raw.trim() !== "") {
const n = Number(raw);
return Number.isFinite(n) ? n : null;
}
return null;
}
Разбор:
- Возврат
nullявно описывают в типе — вызывающий обязан обработать. - Альтернатива —
throwилиResult— 27.md.
Оператор ! (non-null assertion)
function demo(element: HTMLElement | null): void {
element!.focus(); // разработчик уверен, что элемент есть
}
Разбор:
!отключает проверку наnull/undefined— ошибка возможна в runtime.- Используйте редко; лучше
if (element) element.focus().
Ветвления и тип never
function assertNever(x: never): never {
throw new Error(`Unexpected value: ${x}`);
}
type Shape = { kind: "circle"; r: number } | { kind: "square"; a: number };
function area(s: Shape): number {
switch (s.kind) {
case "circle":
return Math.PI * s.r ** 2;
case "square":
return s.a ** 2;
default:
return assertNever(s);
}
}
Разбор:
assertNeverвdefaultломает сборку, если забыли ветку — удобно в библиотечном коде.
Частые ошибки
| Ошибка | Причина | Что делать |
|---|---|---|
| Поле union недоступно | нет narrowing | switch по дискриминанту |
switch без break в JS-стиле | fall-through редко нужен | return в ветках или break |
|| для дефолта числа | 0 ложный | ?? |
as вместо проверки | небезопасно | typeof, predicate |
Забыли default: never | новый вариант union тихо проходит | exhaustive check |
== вместо === | неявное приведение | строгое сравнение |
Практика
- Напишите
parseInput(raw: unknown): number | nullсtypeofи проверкойNumber.isFinite. - Опишите union
Notificationс полемtypeиhandle(n)сswitch+never. - Перепишите цепочку
if/elseнаswitchпоstatusиз 17.md. - Добавьте type predicate
isUserи функцию, принимающуюunknown. - Найдите в проекте
||для дефолта и замените на??там, где допустимы0и"".
Смежные статьи
- Типы — union,
never,LoadState - Переменные — optional chaining
- Циклы —
forс условиями - Асинхронность — union состояний загрузки
- Обработка ошибок — Result, throw
- Справочник — Справочник — объединения и пересечения
- JS: 20