TypeError: Cannot destructure property of undefined в JavaScript — как исправить

опубликовано
читать
≈ 7 мин
автор
среда и версия
JavaScript (ECMAScript 2026)
содержание решать задачи Современный JavaScript

TypeError: Cannot destructure property 'name' of 'value' as it is undefined означает, что JavaScript получил undefined там, где объектный шаблон ожидал значение для разбора. Ошибка возникает до создания переменных из шаблона: извлекать свойства ещё не из чего.

Чаще всего источник — пропущенный аргумент функции, неудачный поиск конфигурации или ответ API без ожидаемого объекта. Исправление зависит от контракта: необязательному объекту задают осмысленное значение по умолчанию, а обязательный проверяют на границе и отклоняют с понятной ошибкой.

JavaScript / 01

Значение по умолчанию срабатывает только для undefined

Значение по умолчанию срабатывает только для undefined01 connect() undefined → {} timeout → 5000 Сначала срабатывает умолчание всего параметра, затем — свойства. 02 connect(null) null остаётся null → TypeError Проверь входные данные или явно сохрани ошибку. {} не подставится.01connect()undefined → {}timeout → 5000Сначала срабатывает умолчание всегопараметра, затем — свойства.02connect(null)null остаётся null→ TypeErrorПроверь входные данные или явно сохраниошибку. {} не подставится.
В function connect({ timeout = 5000 } = {}) два независимых значения по умолчанию: первое заменяет весь аргумент только при undefined, второе — отсутствующее свойство timeout.

Что именно не удалось деструктурировать

В объектной деструктуризации выражение справа вычисляется раньше, чем свойства связываются с переменными:

const result = undefined
const { name } = result
// TypeError: Cannot destructure property 'name' of 'result' as it is undefined

Формулировка зависит от движка и контекста. В сообщении могут быть Cannot destructure property, Cannot destructure или undefined is not an object. Диагностика одна: найди значение справа от = либо фактический аргумент, который попал в деструктурированный параметр.

Пустой объект деструктурируется без ошибки, а отсутствующее свойство получает undefined:

const { name } = {}

console.log(name) // undefined

Значит, сбой вызывает не отсутствие свойства name, а всё исходное значение undefined или null. Другие примитивы для объектного шаблона приводятся к объектам; например, из строки можно извлечь length. Но такой код редко соответствует прикладному контракту, поэтому входные данные всё равно стоит проверять по ожидаемой форме.

Пропущенный аргумент функции становится undefined

Если функцию вызывают без аргумента, соответствующий параметр получает undefined. Деструктуризация выполняется при входе в функцию, поэтому тело ещё не успевает запуститься:

function connect({ url, timeout }) {
  return openConnection(url, timeout)
}

connect()
// TypeError: Cannot destructure property 'url' of 'undefined'

Если весь объект настроек необязателен, задай значение по умолчанию самому параметру:

function connect({ timeout = 5000 } = {}) {
  return openConnection('/status', timeout)
}

connect()
connect({ timeout: 1000 })

Запись = {} после закрывающей фигурной скобки относится ко всему аргументу. Она срабатывает для connect() и connect(undefined). Внутреннее timeout = 5000 относится только к свойству: оно срабатывает, если свойства нет или его значение равно undefined.

Если url обязателен, пустой объект уже не сохраняет контракт. Функция продолжит работу с url === undefined, и ошибка появится позже. В таком случае принимай объект целиком, проверяй его, а затем деструктурируй:

function connect(options) {
  if (options === null || typeof options !== 'object' || Array.isArray(options)) {
    throw new TypeError('Параметр options должен быть объектом')
  }

  const { url, timeout = 5000 } = options

  if (typeof url !== 'string' || url.length === 0) {
    throw new TypeError('Свойство options.url должно быть непустой строкой')
  }

  return openConnection(url, timeout)
}

Такая проверка переносит сбой к границе функции и объясняет, какой договор нарушен. Она не нужна перед каждой внутренней деструктуризацией: после проверки остальной код вправе полагаться на форму options.

Почему null не включает значение параметра по умолчанию

Параметр по умолчанию применяется только к undefined. Поэтому connect(null) не превращает null в пустой объект:

function connect({ timeout = 5000 } = {}) {
  return timeout
}

connect(undefined) // 5000
connect(null) // TypeError

Это различие сохраняет смысл данных. undefined часто получается из-за пропущенного аргумента, а null нередко передают намеренно как отдельное состояние. Если по контракту оба значения означают «настроек нет», нормализуй их явно до деструктуризации:

function connect(options) {
  const { timeout = 5000 } = options ?? {}
  return timeout
}

Такое исправление уместно только при разрешённом отсутствии настроек. Если null означает повреждённый ответ или ошибку вызывающего кода, ?? {} сотрёт полезный сигнал. Тогда оставь ранний TypeError или выбрось свой с описанием контракта.

Значение объекта и значение свойства по умолчанию решают разные задачи

Эти две записи не взаимозаменяемы:

const { theme = 'system' } = settings
const { theme } = settings ?? { theme: 'system' }

В первой строке settings обязан существовать. Значение 'system' используется, когда settings.theme отсутствует или равно undefined. Если settings.theme === null, переменная theme тоже получит null.

Во второй строке запасной объект используется при settings === undefined и settings === null. Но если settings существует как {}, свойство theme останется undefined. Обычно намерение яснее в одной записи:

const { theme = 'system' } = settings ?? {}

Здесь settings ?? {} отвечает за отсутствие всего объекта, а theme = 'system' — за отсутствие свойства. Прежде чем совмещать их, реши отдельно, допустимо ли каждое из этих состояний.

Не заменяй ?? оператором || без причины. Выражение value || {} также подменяет false, 0 и пустую строку. Это расширяет набор принимаемых значений и может скрыть передачу данных неверного типа.

Поиск конфигурации вернул undefined

Чтение по несуществующему ключу и Array.prototype.find() могут вернуть undefined. Ошибка появляется на следующей операции, если результат сразу деструктурируют:

const environments = {
  development: { apiUrl: 'http://localhost:3000' },
  production: { apiUrl: 'https://api.example.com' },
}

const { apiUrl } = environments[currentEnvironment]

Если неизвестное окружение — ошибка конфигурации, не подставляй {}. Проверь результат рядом с поиском:

const environment = environments[currentEnvironment]

if (environment === undefined) {
  throw new Error(`Неизвестное окружение: ${currentEnvironment}`)
}

const { apiUrl } = environment

Запасная конфигурация допустима, только если она предусмотрена требованиями. Тогда выбери её явно, чтобы читатель кода видел правило:

const environment = environments[currentEnvironment] ?? environments.development
const { apiUrl } = environment

У find() выбор тот же: законное отсутствие результата обрабатывают отдельной веткой, а обязательный результат проверяют и отклоняют. const { id } = items.find(...) ?? {} лишь заменит раннюю ошибку на id === undefined.

Ответ API проверяй до деструктуризации

JSON-ответ может быть синтаксически корректным, но содержать null, массив или объект без обязательных полей. Деструктуризация не проверяет прикладную схему. Она только извлекает свойства из полученного значения.

const payload = await response.json()

if (payload === null || typeof payload !== 'object' || Array.isArray(payload)) {
  throw new TypeError('Ответ API должен быть объектом')
}

const { user } = payload

if (user === null || typeof user !== 'object' || Array.isArray(user)) {
  throw new TypeError('В ответе API отсутствует объект user')
}

const { id, name } = user

В проекте с уже установленным валидатором схем используй его на границе ответа. Не пиши параллельный набор ручных проверок в каждом потребителе. После единой проверки функции внутри приложения получают данные известной формы и не маскируют сбои через ?? {}.

Иногда API честно возвращает null, например когда поиск ничего не нашёл. Тогда контракт потребителя должен включать эту ветку:

const user = await loadUser(userId)

if (user === null) {
  return renderNotFound()
}

const { name } = user
return renderProfile(name)

Как выбрать исправление, которое сохраняет контракт

Сначала определи, что означает отсутствующий объект именно в этом месте:

  1. Если аргумент необязателен и пустой объект — допустимое состояние, используй function fn({ option } = {}).
  2. Если null и undefined одинаково означают законное отсутствие, нормализуй значение через value ?? {} до деструктуризации.
  3. Если объект обязателен, проверь его сразу после API, поиска, чтения конфигурации или входа в публичную функцию и останови выполнение с понятной ошибкой.
  4. Если отсутствие — отдельный результат операции, обработай его веткой if и не деструктурируй до этой проверки.

Запасной объект хорош только тогда, когда он сам соответствует допустимому состоянию. Иначе он убирает стек в точке нарушения, но оставляет программе переменные со значением undefined.

Чем эта ошибка отличается от похожих TypeError

Ошибка деструктуризации относится к шаблону const { name } = value или параметру function fn({ name }): весь источник должен допускать извлечение свойств до того, как появится переменная name.

В Cannot read properties of undefined сбой происходит при обычном доступе value.name. Там ищут отсутствующее значение слева от точки в цепочке. Здесь сначала проверяют целиком правую часть деструктуризации и место, где задано значение по умолчанию.

В Cannot set properties of undefined код пытается записать свойство выражением value.name = 'Анна'. Это не создание переменных по шаблону: исправлять нужно объект-получатель записи или ветку, которая должна была его создать.

Короткий порядок диагностики

  1. Найди первый кадр стека из своего кода и сам шаблон { ... } слева от = либо в параметрах функции.
  2. Выведи значение справа от = или фактический аргумент до вызова.
  3. Проследи источник undefined или null: пропущенный аргумент, поиск, ключ конфигурации, ответ API или функция без ожидаемого результата.
  4. Реши по контракту, допустимо ли отсутствие всего объекта и отдельных свойств.
  5. Поставь = {} или ?? {} только для допустимого отсутствия; обязательные данные проверь на границе.

Главное различие — уровень значения по умолчанию. Внешнее = {} спасает от пропущенного объекта, внутреннее property = value заполняет отсутствующее свойство, а null не запускает ни одно из них автоматически.

Источники