TypeError: Cannot read properties of undefined (reading 'name') означает, что JavaScript попытался прочитать свойство name у значения undefined. В длинной цепочке проблема находится не обязательно в name: значение слева от последней точки могло пропасть раньше.
Для null причина та же. V8 обычно пишет Cannot read properties of null, Firefox — null has no properties, Safari формулирует сообщение иначе. Во всех случаях это TypeError: код обращается к свойству значения, у которого свойств нет.
reading name: проверяй объект перед .name
payload.user.profile.name сообщение называет свойство name, но проверять нужно значение слева: первым пропавшим звеном оказался user.profile.Что именно означает Cannot read properties of undefined
У обычного объекта чтение отсутствующего свойства не вызывает исключение: результатом будет undefined. Ошибка появляется, когда код пытается продолжить цепочку и прочитать свойство уже у этого значения.
const user = {}
console.log(user.profile) // undefined
console.log(user.profile.name) // TypeError: Cannot read properties of undefined
В выражении user.profile.name движок сначала вычисляет user.profile. Получается undefined, после чего выполняется чтение .name. Именно в этот момент возникает TypeError.
С null происходит то же самое:
const user = { profile: null }
console.log(user.profile.name) // TypeError: Cannot read properties of null
По смыслу undefined обычно обозначает отсутствующее значение, а null — намеренное отсутствие объекта. Для этой ошибки различие не влияет на результат: читать свойства нельзя у обоих значений. При этом typeof null возвращает строку 'object' по исторической причине, поэтому проверка только через typeof value === 'object' недостаточна.
Как быстро найти значение, которое пропало
Начните со стека ошибки, а не с добавления ?. во все цепочки. Нужен первый кадр из кода приложения: он показывает файл, строку и выражение, которое выполнялось. Часть (reading 'name') называет свойство, которое движок пытался прочитать, но не источник undefined.
Затем разделите длинное выражение на промежуточные значения:
const user = payload?.user
const profile = user?.profile
console.log({ payload, user, profile })
console.log(profile.name)
Optional chaining здесь используется только как диагностический приём: он позволяет увидеть undefined, не остановившись на предыдущем звене. В отладчике можно поставить точку останова на строке ошибки и по очереди вычислить payload, payload.user и payload.user.profile.
После первого найденного undefined или null идите к месту, где это значение появилось:
- Какую функцию, запрос или поиск оно получило?
- Допускает ли контракт отсутствие результата?
- Если не допускает, где данные потерялись: при создании объекта, загрузке, преобразовании или передаче аргумента?
Такой порядок короче случайных правок. Он отделяет место падения от источника неверного значения.
Поиск не нашёл элемент
Array.prototype.find() возвращает первый подходящий элемент, а если совпадения нет — undefined. Следующее чтение свойства и даёт ошибку:
const users = [{ id: 1, name: 'Аня' }]
const user = users.find((item) => item.id === 7)
console.log(user.name) // TypeError: Cannot read properties of undefined
Если отсутствие пользователя — нормальный исход, обработайте его сразу после поиска:
function getUserName(users, requestedId) {
const user = users.find((item) => item.id === requestedId)
if (user === undefined) {
return 'Пользователь не найден'
}
return user.name
}
Если пользователь обязан существовать, запасная строка только скроет нарушение контракта. В таком случае выбросьте понятную ошибку с идентификатором или исправьте данные, из-за которых поиск ничего не находит.
Тот же принцип относится к чтению отсутствующего ключа объекта и индекса за пределами массива: сначала проверьте контракт операции, которая вернула значение, затем решайте, допустимо ли отсутствие.
Объект читают до инициализации
Переменная может быть объявлена, но ещё не получить объект. Частая причина — неверный порядок вызовов:
let settings
renderTheme(settings.theme)
settings = loadSettings()
Переставить строки недостаточно, если loadSettings() асинхронна. Но для синхронной инициализации исправление прямое: сначала создать значение, затем передать его потребителю.
const settings = loadSettings()
renderTheme(settings.theme)
Ещё один источник — объект разной формы в разных ветках:
let session
if (hasToken) {
session = { user: currentUser }
}
showName(session.user.name)
Здесь нужно определить оба состояния явно. Если без токена показывается гостевой экран, верните его до чтения session.user. Если токен обязателен, остановите выполнение с понятной ошибкой. Присваивание session = {} перед if лишь передвинет падение с session.user на session.user.name.
Асинхронные данные ещё не готовы
Код после запуска промиса продолжает выполняться. Поэтому переменная, которую присвоят в then(), остаётся undefined до завершения операции:
let account
loadAccount().then((value) => {
account = value
})
showName(account.profile.name)
Свяжите чтение с результатом промиса. В async-функции это обычно один await:
async function showAccount() {
const account = await loadAccount()
showName(account.profile.name)
}
await решает только вопрос порядка. Сервер всё равно может вернуть объект без profile, поэтому ответ внешнего API нужно проверить перед передачей в основную логику. Не лечите гонку произвольным setTimeout: задержка не создаёт гарантии, что операция уже завершилась.
DOM-поиск вернул null
document.getElementById() возвращает null, если элемента с таким id нет в документе. Причиной может быть опечатка в идентификаторе, запуск скрипта до создания разметки или страница, на которой этот элемент не предусмотрен.
const heading = document.getElementById('profile-name')
const currentName = heading.textContent
Если элемент обязателен для этой страницы, проверьте результат рядом с DOM-поиском:
const heading = document.getElementById('profile-name')
if (heading === null) {
throw new Error('Не найден элемент #profile-name')
}
const currentName = heading.textContent
Если элемент необязателен, ветка if (heading !== null) точнее описывает намерение. Но сначала проверьте, почему поиск ничего не нашёл: регистр id учитывается, а сам элемент должен уже находиться в дереве документа.
Исправляйте данные на границе
Лучшее место для проверки — там, где ненадёжные данные входят в программу: после ответа API, чтения хранилища, поиска элемента или вызова функции, которая допускает пустой результат. Тогда внутренний код получает объект известной формы и не повторяет одинаковые проверки.
Для небольшого фрагмента достаточно обычных условий:
const payload = await response.json()
if (payload === null || typeof payload !== 'object') {
throw new TypeError('Ответ API должен быть объектом')
}
const user = payload.user
if (user === null || typeof user !== 'object') {
throw new TypeError('В ответе API отсутствует user')
}
const profile = user.profile
if (profile === null || typeof profile !== 'object') {
throw new TypeError('В ответе API отсутствует user.profile')
}
if (typeof profile.name !== 'string') {
throw new TypeError('user.profile.name должен быть строкой')
}
showName(profile.name)
В проекте с готовой схемой валидации используйте её, а не пишите второй валидатор. Важно не название инструмента, а момент проверки: до того, как неполный объект разойдётся по приложению.
Отсутствие иногда входит в контракт. Например, незаполненный город профиля можно показать как «не указан». Тогда явная запасная ветка корректна:
const city = profile.address?.city ?? 'Не указан'
Для обязательного profile.name такая подстановка уже меняет смысл: вместо ошибки данных интерфейс молча покажет гостя или пустую строку.
Почему optional chaining и значения по умолчанию могут скрыть дефект
Оператор ?. прекращает непрерывную цепочку, если слева null или undefined, и возвращает undefined вместо исключения. Оператор ?? подставляет правую часть только для этих двух значений.
const name = response?.user?.profile?.name ?? 'Гость'
Этот код безопасен, если по правилам продукта user, profile и name действительно могут отсутствовать, а «Гость» — предусмотренное отображение. Если хотя бы одно поле обязательно, выражение скрывает повреждённый ответ: стек исчезает, интерфейс выглядит рабочим, а причину сложнее найти.
Подстановка пустого объекта создаёт похожий эффект:
const profile = response.user?.profile ?? {}
const normalizedName = profile.name.toUpperCase()
Ошибка не исправлена, а перенесена на следующую строку: теперь profile.name равно undefined. Запасное значение должно быть полноценным допустимым состоянием, а не способом убрать красное сообщение из консоли.
Используйте ??, а не ||, когда запасное значение нужно только для null и undefined. Оператор || также заменит корректные 0, false и пустую строку.
Похожие TypeError требуют другой диагностики
Эта статья разбирает чтение: value.property. У похожих сообщений другой момент сбоя:
Cannot set properties of undefinedвозникает при записиvalue.property = ...; сначала проверяют объект слева от присваивания.- Ошибка деструктуризации
undefinedилиnullпоявляется, когда код извлекает свойства конструкцией вродеconst { name } = value; там важны источник значения и значение по умолчанию для всей правой части. is not a functionозначает, что выражение дошло до вызова, но найденное значение нельзя вызвать. Это не обязательноundefined: в свойстве может лежать строка, объект или число.
Разделение по намерению экономит время: чтение, запись, деструктуризация и вызов проходят разные операции языка, даже если рядом стоит одно и то же имя свойства.
Чек-лист диагностики
- Найдите в стеке первую строку из кода приложения. Если стек состоит из тысяч повторов одной и той же строки, ошибка другая — это Maximum call stack size exceeded, и чинить надо рекурсию.
- Выпишите свойство из части
(reading '...'). - Разбейте цепочку и найдите первое значение
undefinedилиnull. - Проследите, откуда оно пришло: поиск, инициализация, промис, API или DOM.
- Решите по контракту, допустимо ли отсутствие.
- Обработайте ожидаемое отсутствие сразу после операции, которая его возвращает.
- Для обязательных данных выбросьте понятную ошибку или исправьте источник.
- Добавляйте
?.,??и пустые значения только там, где они описывают реальное состояние продукта.
Главный ориентир — значение слева от свойства. Найдите первое пропавшее звено, затем исправьте место, где оно создаётся или входит в программу. Так ошибка исчезнет вместе с причиной, а не только с конкретной строкой в стеке.