Avatar ·

Поддерживает ли TypeScript оператор ?. и как он правильно называется?

Всем привет! Я недавно начал изучать TypeScript и столкнулся с проблемой. Часто в коде мне нужно обратиться к свойству объекта, которого может не существовать. Например, у меня есть объект `user`, который приходит с сервера, и у него может быть вложенное поле `profile.name`. Если я пишу `user.profile.name`, то TypeScript выдает ошибку, что `profile` может быть `undefined`. Я знаю, что в JavaScript есть оператор `?.`, который позволяет безопасно обращаться к свойствам. Но я не уверен, поддерживает ли его TypeScript и как он правильно называется. Я пробовал писать так: ```typescript interface User { profile?: { name?: string; }; } const user: User = {}; console.log(user.profile.name); // Ошибка: Object is possibly 'undefined' ``` Если я добавляю `?.`, то ошибка исчезает: ```typescript console.log(user?.profile?.name); ``` Но я не понимаю, это фича TypeScript или просто JavaScript? И как правильно называется этот оператор — "опциональная цепочка", "вопросительный оператор" или что-то еще? Также интересно, можно ли его использовать с функциями? Буду благодарен за подробное объяснение с примерами, так как хочу писать более безопасный код без лишних проверок на `if`. <<>>

Что такое оператор ?. (опциональная цепочка) в TypeScript?

Да, TypeScript полностью поддерживает оператор ?., который называется опциональная цепочка (optional chaining). Этот оператор был представлен в TypeScript 3.7 и с тех пор является стандартной частью языка. Важно понимать, что это не только фича TypeScript — это нативная возможность JavaScript, начиная с ES2020. TypeScript просто добавляет к ней строгую типизацию.

Как это работает?

Оператор ?. позволяет безопасно обращаться к вложенным свойствам объекта, не проверяя каждое промежуточное значение на null или undefined. Если какая-либо часть цепочки оказывается null или undefined, то выражение сразу возвращает undefined, не вызывая ошибки.

Рассмотрим ваш пример:

interface User {
  profile?: {
    name?: string;
  };
}

const user: User = {};
console.log(user?.profile?.name); // Выведет: undefined

Без ?. вы бы получили ошибку, так как user.profile — это undefined, и попытка обратиться к .name у undefined вызовет исключение. С опциональной цепочкой код безопасен.

Где можно использовать ?.

  • Свойства объектов: obj?.prop
  • Индексы массивов: arr?.[index]
  • Вызов функций: func?.(args) — вызывает функцию только если она существует

Примеры с функциями

Опциональная цепочка работает и с вызовом функций. Это полезно, когда вы не уверены, что метод существует:

interface ApiResponse {
  data?: {
    items?: Array<{ id: number }>;
    getItem?: (id: number) => { id: number } | undefined;
  };
}

const response: ApiResponse = {};

// Безопасный вызов метода
const item = response.data?.getItem?.(5);
console.log(item); // undefined, но ошибки не будет

// Безопасный доступ к элементу массива
const firstItem = response.data?.items?.[0];
console.log(firstItem); // undefined

Разница между ?. и !. (non-null assertion)

Важно не путать опциональную цепочку с оператором ! (non-null assertion). ! говорит TypeScript: "Я уверен, что это значение не null и не undefined", но не добавляет никакой проверки в рантайме:

// Опасно! Если profile = undefined, код упадет
console.log(user.profile!.name); // Ошибка в рантайме, если profile нет

// Безопасно
console.log(user.profile?.name); // Просто вернет undefined

Комбинация с оператором ?? (nullish coalescing)

Часто ?. используют вместе с ?? (оператор нулевого слияния), чтобы задать значение по умолчанию:

const userName = user?.profile?.name ?? 'Гость';
console.log(userName); // 'Гость', так как profile.name = undefined

Ограничения

Опциональную цепочку нельзя использовать для левой части присваивания. Например, user?.name = 'John' вызовет ошибку. Также будьте осторожны с производительностью в циклах — каждая проверка добавляет небольшой overhead, но в 99% случаев это несущественно.

В итоге, ?. — это мощный инструмент для написания чистого и безопасного кода. Используйте его вместо длинных цепочек if-проверок, и ваш код станет гораздо читаемее.

<<>>

Альтернативный подход: логические операторы и утилитарные функции

Хотя опциональная цепочка — это современный и удобный способ, вы можете столкнуться с кодом, где её не используют, особенно в старых проектах или если нужно поддерживать очень старые версии JavaScript (до ES2020). В таких случаях применяют традиционные проверки:

// Старый способ с логическими операторами
const userName = user && user.profile && user.profile.name || 'Гость';

// Или более читаемый вариант с if
let userName = 'Гость';
if (user && user.profile && user.profile.name) {
  userName = user.profile.name;
}

Также можно написать вспомогательную функцию для глубокого доступа:

function getSafe(obj: any, path: string, defaultValue?: any): any {
  return path.split('.').reduce((acc, key) => {
    return acc && acc[key] !== undefined ? acc[key] : undefined;
  }, obj) ?? defaultValue;
}

const name = getSafe(user, 'profile.name', 'Гость');

Этот подход более гибкий, но менее производительный и читаемый. Рекомендую использовать ?. везде, где это возможно, так как это стандарт современного TypeScript и JavaScript.