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);
}
Answer
- We can use short circuit evaluation to supply a default if
user
is a falsy value (undefined
,null
,0
,-0
,0n
,""
orNaN
).
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
```
- Another approach is to spread the
user
into an object before destructuring it, becausenull
andundefined
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
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);
}
TLDR
- Retrieving a property (that cannot be falsy) with destructuring from a value that can be an
object
orundefined
- use short circuit evaluation. - Retrieving a property (that can be falsy) with destructuring from a value that can be an
object
orundefined
- 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.
Top comments (0)