JS/ES6: Destructuring of undefined

I'm using some destructuring like this:

const { item } = content
console.log(item)

But how should I handle content === undefined - which will throw an error?

The 'old' way would look like this:

const item = content && content.item

So, if content is undefined -> item will also be undefined.

Can I do something similar using destructuring?


You can use short circuit evaluation to supply a default if content is a falsy value, usually undefined or null in this case.

const content = undefined
const { item } = content || {}
console.log(item)                       // undefined

A less idiomatic (see this comment) way is to spread the content into an object before destructuring it, because null and undefineds values are ignored.

const content = undefined
const { item } = { ...content }
console.log(item) // undefined

If you are destructuring function params you can supply a default (= {} in the example).

Note: The default value would only be applied if the destructured param is undefined, which means that destructuring null values will throw an error.

const getItem = ({ item } = {}) => item
console.log(getItem({ item: "thing" })) // "thing"
console.log(getItem())                  // undefined

try {
  getItem(null)
} catch(e) {
  console.log(e.message)                // Error - Cannot destructure property `item` of 'undefined' or 'null'.
}

Or even set a default value for the item property if the input object doesn't contain the property

const getItem = ({ item = "default" } = {}) => item
console.log(getItem({ item: "thing" })) // "thing"
console.log(getItem({ foo: "bar" }))    // "default"

const { item } = Object(content)

One can unpack undefined value, but can't unpack from undefined.
Fixing it is as easy as setting the default params value.

Example:

(() => {
    // prepare payload
    const PAYLOAD = {
        holdingJustThis: 1
    };
    // lets unpack the payload and more
    const {
        holdingJustThis,
        itIsGoingToBeUndefined,
        itCouldThrowButWont: {
            deep
        } = {}                  // this will secure unpacking "deep"
    } = PAYLOAD;

    console.log({
        holdingJustThis
    });
    console.log({
        itIsGoingToBeUndefined  // logs {itIsGoingToBeUndefined:undefined}
    });
    console.log({
        deep                    // logs {deep:undefined}
    });
})()

Destructuring the nested object is clean and short but sucks when source property is null or undefined in right side object

let say we have

const {
  loading,
  data: { getPosts },
} = useQuery(FETCH_POSTS_QUERY);

Solution 1 if we have data object but no getPosts then we can use:
(Setting default at each level)

const {
  loading,
  data: { getPosts = [] } = { getPosts: [] },
} = useQuery(FETCH_POSTS_QUERY);

Solution 2: if event data is undefined then:

const {
  loading,
  data: { getPosts } = { getPosts: [] },
} = useQuery(FETCH_POSTS_QUERY);