ПрограммированиеFrontend разработчик

Как работает механизм optional chaining (опциональная цепочка доступа) в TypeScript? Какие проблемы он решает и в каких случаях его использование может привести к ошибкам на практике?

Проходите собеседования с ИИ помощником Hintsage

Ответ.

Механизм optional chaining (?.) появился в JavaScript для удобного безопасного доступа к свойствам и методам объектов, которые могут быть необязательно определены. В TypeScript он стал особенно полезен, поскольку помогает избежать ошибок времени исполнения, связанных с обращением к свойствам или методам undefined или null.

История вопроса

До появления optional chaining разработчикам приходилось вручную проверять существование каждого уровня вложенности объекта, что делало код длинным и неудобочитаемым:

if (obj && obj.a && obj.a.b) { // ... }

Проблема

При обращении к вложенным свойствам у неопределённого объекта возможна ошибка времени исполнения: Cannot read property 'x' of undefined. Кроме того, длинные цепочки проверок затрудняют поддержку и воспринимаемость кода.

Решение

Optional chaining (?.) позволяет обращаться к вложенным свойствам, методам или элементам массива, автоматически возвращая undefined, если какая-либо часть цепочки равна null или undefined.

Пример кода:

interface User { name: string; address?: { city?: string; }; } const user: User = { name: 'Ivan' }; console.log(user.address?.city); // undefined

Ключевые особенности:

  • Позволяет писать компактный, безопасный и понятный код.
  • Работает с доступом к свойствам, методам, индексам массивов.
  • Тип возвращаемого значения — T | undefined, что учитывается компилятором TypeScript и помогает избежать ошибок.

Вопросы с подвохом.

Можно ли использовать optional chaining слева от оператора присваивания? Например: user.address?.city = 'Moscow'?

Нет, optional chaining нельзя использовать в левом выражении присваивания, это вызовет ошибку компиляции. Optional chaining работает только при чтении, а не при записи.

Можно ли воспользоваться optional chaining для вызова метода, если сам объект может быть не определён? Например: user?.logInfo()?

Да, если объект до точки вызова метода может быть undefined/null, то вызов user?.logInfo() не выбросит ошибку и просто вернёт undefined, если user не определён.

user?.logInfo(); // если user определён, вызовется logInfo, иначе ничего не произойдёт

В чём различие между optional chaining и оператором '&&' в старом стиле, например: user && user.address && user.address.city?

Optional chaining короче, работает для всех типов (включая методы и массивы), а также TypeScript учитывает его при типовой проверке. Важно, что при использовании оператора '&&' вы можете нечаянно получить не true/false или undefined, а вложенное значение. Optional chaining же гарантированно даёт либо ожидаемый тип, либо undefined, что учитывается выводом типов.

Типовые ошибки и анти-паттерны

  • Ожидание, что optional chaining "создаёт" вложенные объекты при присваивании — это не так.
  • Использование optional chaining при обращении к переменным, которые по типу никогда не могут быть undefined/null (лишний и бесполезный код).
  • Привычка бездумно применять optional chaining везде, что затрудняет дальнейшее сопровождение кода.

Пример из жизни

Негативный кейс

В коде используются длинные цепочки optional chaining даже там, где переменные по бизнес-логике всегда определены:

order?.customer?.address?.city = 'London';

Плюсы:

  • Защищает от возможных ошибок, если свойства всё же окажутся не определены.

Минусы:

  • Присваивание не произойдёт, если хоть одно из свойств отсутствует, при этом нет уведомления или обработки ошибки; появляется "скрытая" логика, спутанность кода.

Позитивный кейс

Используется optional chaining только при работе с внешними данными или в местах, где значения действительно могут быть неопределёнными:

const city = apiResponse?.info?.location?.city || 'Unknown';

Плюсы:

  • Безопасно работает с даже неполными или неожиданно изменившимися объектами без выбрасывания исключений.
  • Простой для чтения и поддержки код.

Минусы:

  • Если требуется знать, почему значение отсутствует, приходится делать явную обработку ошибок.