Оператор ?. в TypeScript: как он работает и как называется на самом деле

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: вопрос, который мучает новичков

Вы когда-нибудь писали на TypeScript и получали ошибку вроде Cannot read property 'name' of undefined? Знакомая боль, правда? Особенно когда работаешь с данными, приходящими с сервера, или с глубоко вложенными объектами. Именно в такие моменты и возникает вопрос: «А есть ли в TypeScript оператор, который позволяет безопасно обращаться к свойствам, не боясь, что всё упадёт?»

На Stack Overflow этот вопрос собрал тысячи просмотров. И ответ — да, такой оператор есть. Более того, он есть и в JavaScript (начиная с ES2020), и в TypeScript он работает «из коробки». Называется он optional chaining (опциональная цепочка), а выглядит как знакомый каждому ?.. В этой статье мы не только расскажем, как его использовать, но и заглянем под капот: как компилятор TypeScript превращает этот элегантный синтаксис в безопасный JavaScript-код.

Как называется оператор ?. в TypeScript?

Начнём с главного. В сообществе разработчиков этот оператор называют по-разному:

  • Optional chaining (опциональная цепочка) — официальное название в спецификации ECMAScript.
  • Оператор безопасного доступа (Safe navigation operator) — пришло из других языков, например, C# и Swift.
  • Null-conditional operator — встречается в документации Microsoft.

Но суть одна: ?. позволяет обратиться к свойству объекта, и если объект равен null или undefined, то вместо ошибки мы просто получаем undefined. Это не магия, а просто синтаксический сахар, который делает код чище и безопаснее.

Базовый синтаксис

Синтаксис предельно прост. Вместо того чтобы писать:

if (user && user.profile && user.profile.name) {
    console.log(user.profile.name);
}

Вы пишете:

console.log(user?.profile?.name);

Если user — undefined, то вся цепочка прерывается, и в консоль выводится undefined. Никаких ошибок, никаких лишних проверок. Красота!

Как это работает под капотом: компиляция TypeScript в JavaScript

TypeScript — это надстройка над JavaScript. Он не выполняет код напрямую, а компилирует его в обычный JS. Поэтому давайте посмотрим, во что превращается наш элегантный user?.profile?.name после компиляции.

Пример: простой optional chaining

Исходный код на TypeScript:

const name = user?.profile?.name;

После компиляции (цель — ES5):

var name = user === null || user === void 0 ? void 0 : user.profile === null || user.profile === void 0 ? void 0 : user.profile.name;

Что здесь происходит? Компилятор генерирует тернарный оператор, который проверяет каждый уровень вложенности на null или undefined. Если значение null или undefined, возвращается void 0 (это просто надёжный способ получить undefined на старых движках). Если нет — идём дальше. По сути, это тот самый «ручной» код, который мы писали раньше, но теперь его генерирует машина.

Пример: optional chaining с вызовом функции

Оператор ?. работает не только со свойствами, но и с вызовами функций. Допустим, у нас есть метод getAddress, который может отсутствовать:

const address = user?.getAddress?.();

После компиляции:

var address = user === null || user === void 0 ? void 0 : (typeof user.getAddress === "function" ? user.getAddress() : void 0);

Обратите внимание: компилятор не просто проверяет null, но и удостоверяется, что getAddress — это функция, прежде чем её вызвать. Если это не функция, возвращается undefined. Это защищает от ошибок, когда свойство существует, но не является вызываемым.

Почему это полезно в реальных проектах?

Давайте рассмотрим несколько сценариев, где optional chaining спасает день.

1. Работа с API

Представьте, что вы получаете от сервера данные пользователя. Не всегда все поля присутствуют. Без optional chaining вам пришлось бы писать горы проверок:

if (response && response.data && response.data.user && response.data.user.name) {
    // ...
}

С optional chaining:

const userName = response?.data?.user?.name;

Код стал читаемым и лаконичным. А если данные пришли неполные — просто получите undefined, и приложение не упадёт.

2. Конфигурация с опциональными настройками

Часто у нас есть объект конфигурации, где некоторые опции могут отсутствовать. Optional chaining позволяет обращаться к ним без страха:

const theme = config?.ui?.theme?.primaryColor ?? 'blue';

Обратите внимание на ?? (nullish coalescing) — это ещё один полезный оператор, который часто идёт в паре с ?.. Он возвращает правую часть только если левая равна null или undefined.

3. Динамические ключи

Optional chaining работает и с динамическими ключами через квадратные скобки:

const value = obj?.[key];

Это особенно удобно, когда вы работаете с Map или словарями.

Ограничения и подводные камни

Как и любой инструмент, optional chaining нужно использовать с умом. Вот несколько моментов, о которых стоит помнить:

  • Не заменяет валидацию. Если вам нужно вернуть пользователю сообщение об ошибке, а не просто undefined, лучше использовать явные проверки.
  • Не используйте на левой стороне присваивания. Написать user?.name = 'John' — нельзя. Это вызовет синтаксическую ошибку.
  • Совместимость с браузерами. Если вы компилируете в ES5, компилятор TypeScript сам сгенерирует безопасный код. Но если вы пишете на современном JS и не используете транспилятор, убедитесь, что ваша целевая аудитория использует современные браузеры.

Сравнение с другими языками

Если вы пришли из других языков, вам будет приятно узнать, что концепция не нова:

  • Swift — использует ? для опциональных цепочек.
  • C# — использует ?. (null-conditional operator).
  • Dart — использует ?..

Блоги

Рекомендаций по книгам