DEV Community

loading...
Cover image for TIL: Destructuring from Object | Undefined

TIL: Destructuring from Object | Undefined

stevescruz profile image Steve Cruz Originally published at Medium ・3 min read

Destructure from a value that can be undefined

Problem

The updateUserProfile function allows me to retrieve the user by making a query to the database with the provided user_id, then use another database query to guarantee that the provided email does not belong to another user and finally update the user in the database sending a query with the provided name and email.

The problem was that when checking if we already had a user with the provided email in the database the result could be a User object or undefined, depending on if we found the user or not.

The User object contains many properties (id, name, email, avatar, password, created_at, updated_at), but I only needed the id property to compare with the provided user_id to guarantee that the email did not belong to any user.

I was not able to use destructuring to unpack only the id from the result nor rename it to findEmailOwner because the result could be a User object or undefined, so I got the following TypeScript error message: "Property 'id' does not exist on type 'User | undefined'.

TLDR: I need to obtain id by destructuring a value that can be an object or undefined.

function updateUserProfile ({ user_id, name, email }) {
  const user = await usersRepository.findById(user_id);

  if (!user) {
    throw new AppError(`The user was not found.`, 401);
  }

  const { id: findEmailOwner } = await usersRepository.findByEmail(email); // error message: "Property 'id' does not exist on type 'User | undefined'.

  if (typeof findEmailOwner !== 'undefined' && findEmailOwner !== user_id) {
    throw new AppError(`This email cannot be used.`, 401);
  }

  user.name = name;  
  user.email = email;

  return usersRepository.save(user);
}
Enter fullscreen mode Exit fullscreen mode

Answer

  • We can use short circuit evaluation to supply a default if user is a falsy value (undefined, null, 0, -0, 0n, "" or NaN).

NOTE 1: I can use this approach in my application because the id property that I want to retrieve with destructuring cannot be assigned to any falsy value in my database.

NOTE 2: BUT if I was retrieving the avatar property that can be assigned to null in the database, this approach would not work.

```tsx
// Case 1 - id (cannot contain falsy values)

// user does not exist
const user = undefined
const { id } = user || {}
console.log(id) // undefined (what we expect)

// user exists
const user = {
    id: 'aaaa-aaaa-aaaa-aaaa',
};
const { id } = user || {}
console.log(id) // 'aaaa-aaaa-aaaa-aaaa' (what we expect)

// Result: SUCCESS

//-----------------------------------------

// Case 2 - avatar (can contain null a falsy values)

const user = undefined
const { avatar } = user || {}
console.log(avatar) // undefined (what we expect)

const user = {
    avatar: 'photo.jpg',
};
const { avatar } = user || {}
console.log(avatar) // 'photo.jpg' (what we expect)

const user = {
    avatar: null,
};
const { avatar } = user || {}
console.log(avatar) // undefined (not good, we needed this to be null)

// Result: FAILURE
```
Enter fullscreen mode Exit fullscreen mode
  • Another approach is to spread the user into an object before destructuring it, because null and undefined values are ignored.

NOTE 1: I would use this approach if was retrieving the avatar property that can be assigned to a falsy value (null) in the database since the first approach would not work.

NOTE 2: This approach is less idiomatic, so I would not use it for cases where the first approach works.

NOTE 3: This approach would also work for id.

//Case - avatar (can contain null a falsy values)

const user = undefined
const { avatar } = { ...user }
console.log(avatar) //undefined (what we expect)

const user = {
  avatar: 'picture.jpg',
}
const { avatar } = { ...user }
console.log(avatar) // 'picture.jpg' (what we expect)

const user = {
  avatar: null,
}
const { avatar } = { ...user }
console.log(avatar) // null (what we expect)

// Result: SUCCESS
Enter fullscreen mode Exit fullscreen mode

Applying the short circuit evaluation approach to our code:

function updateUserProfile ({ user_id, name, email }) {
  const user = await usersRepository.findById(user_id);
  if (!user) {
    throw new AppError(`The user was not found.`, 401);
  }
  const { id: findEmailOwner } = (await usersRepository.findByEmail(email)) || {}; // 1st approach
  if (typeof findEmailOwner !== 'undefined' && findEmailOwner !== user_id) {
    throw new AppError(`This email cannot be used.`, 401);
  }
  user.name = name;
  user.email = email;
  return usersRepository.save(user);
}
Enter fullscreen mode Exit fullscreen mode

TLDR

  • Retrieving a property (that cannot be falsy) with destructuring from a value that can be an object or undefined - use short circuit evaluation.
  • Retrieving a property (that can be falsy) with destructuring from a value that can be an object or undefined - use the spread operator on the value that can be an object or undefined.

Additional Links

Keep in touch

Contact me through my social media. Let's talk about DDD, TDD, good practices and the new Wonder Woman 1982 movie, be it on LinkedIn or GitHub.

Tell me what you learned today.

Discussion (0)

pic
Editor guide