loading...
Cover image for 3 Weird Things You (Probably) Didn't Know You Can Do With The JavaScript Spread Operator πŸ₯³

3 Weird Things You (Probably) Didn't Know You Can Do With The JavaScript Spread Operator πŸ₯³

harrison_codes profile image Harrison Reid Updated on ・4 min read

If you find this post useful, you can sign up to my mailing list, check out the other posts on my blog, or follow me on twitter. I've also got a couple of active side projects that you might like to check out:

  • ippy.io - An app for creating beautiful resumes
  • many.tools - A collection of useful utilities for designers and devs

The spread operator has been enthusiastically adopted by the JavaScript community since its inclusion in the language with ES6, with good reason! It vastly simplifies many common object and array manipulations patterns.

While the common uses are widely appreciated and utilised, it also facilitates some slightly more obscure patterns.

Such as…

πŸ‘‰ 1) Conditionally Adding Properties to an Object

It may not be particularly common, but imagine that (for whatever reason) you want to conditionally add properties to an object. Specifically, you want to add the properties if they hold a truthy value, but exclude them if they are null, undefined, or contain a falsey value. How might you approach this?

A reasonable approach might be something like the following:

const firstName = 'Harrison'
const lastName = null
const address = '123 Street Rd'
const phoneNumber = null

const userInfo = {}

if (firstName) {
  userInfo.firstName = firstName
}

if (lastName) {
  userInfo.lastName = lastName
}

if (address) {
  userInfo.address = address
}

if (phoneNumber) {
  userInfo.phoneNumber = phoneNumber
}

console.log(userInfo)

// {
//   firstName: 'Harrison',
//   address: '123 Street Rd'
// }

There's nothing wrong with this approach - however using the spread operator, we can move the conditional logic inside the object literal.

The result is somewhat more concise, and in my opinion once you've seen it a few times is actually more readable.

Take a look:

const firstName = 'Harrison'
const lastName = null
const address = '123 Street Rd'
const phoneNumber = null

const userInfo = {
  ...firstName && { firstName },
  ...lastName && { lastName },
  ...address && { address },
  ...phoneNumber && { phoneNumber }
}

console.log(userInfo)

// {
//   firstName: 'Harrison',
//   address: '123 Street Rd'
// }

If you haven't seen this pattern before, it might take a second to grok what's going on. I'll try to explain:

Lets consider the first line inside the object literal, a case in which the property should be added to the object:

...firstName && { firstName }

Since firstName was previously assigned the truthy value 'Harrison',
the expression firstName && { firstName } will return { firstName: 'Harrison' }. Both the left and right hand side of the && evaluate as truthy, and as such the right hand side is returned.

This returned object is then spread into the userInfo object, resulting in the firstName property being successfully set.

Next, lets consider the alternate case, in which we attempt to assign a falsey value. Lets take the second line of the object literal:

...lastName && { lastName }

In this case, lastName is null. This means that the expression lastName && { lastName } short-circuits to returning the left hand side of the &&, which in this case is null.

We then attempt to spread null into the userInfo object. You might think this should result in an error, but it actually doesn't.

In fact, as far as I'm aware spreading any falsey value into an object is perfectly valid syntax, but will result in no change to the object. Try it out:

let obj = { ...null }
console.log(obj)
// {}

let obj = { ...undefined }
console.log(obj)
// {}

let obj = { ...false }
console.log(obj)
// {}

let obj = { ...'' }
console.log(obj)
// {}

let obj = { ...0 }
console.log(obj)
// {}

let obj = { ...{} }
console.log(obj)
// {}

let obj = { ...[] }
console.log(obj)
// {}

The end result of all this is that any truthy values will be added to the object, while any falsey values are left out!

To make the code more explicit we can use the same pattern, but refactor the truthy check into its own function:

const hasIfTruthy = (propertyName, property) => {
  return property && { [propertyName]: property }
}

const firstName = 'Harrison'
const lastName = null
const address = '123 Street Rd'
const phoneNumber = null

const userInfo = {
  ...hasIfTruthy('firstName', firstName),
  ...hasIfTruthy('lastName', lastName),
  ...hasIfTruthy('address', address),
  ...hasIfTruthy('phoneNumber', phoneNumber)
}

console.log(userInfo)

// {
//   firstName: 'Harrison',
//   address: '123 Street Rd'
// }

Using this pattern, you can even completely alter the condition that dictates whether a property is included or excluded - it doesn't necessarily need to be based on just truthy-ness/falsy-ness.


πŸ‘‰ 2) Spreading an Array into an Object

So… I'm yet to think of a particularly compelling reason that you would actually do this (shout out in the comments if you have one), but you can totally spread an Array into an Object.

The result is that each array element is inserted into the object, with the key set to its respective array index.

Check it out:

const fruitsArray = ['apple', 'orange', 'banano']

const fruitsObject = { ...fruitsArray }

console.log(fruitsObject)

// {
//   0: 'apple',
//   1: 'orange',
//   2: 'banano'
// }


πŸ‘‰ 3) Spreading a String Into an Array (or an Object)

This one is actually pretty nifty, and is probably more widely known than the others. You can spread a string into an array!

The result is an array containing the individual characters from the string.

In my opinion this allows for a more pleasant syntax than the common 'string'.split('') style.

Here it is:

const characters = [..."apple"]

console.log(characters)

// ['a', 'p', 'p', 'l', 'e']

And if you're feeling really wild, you can even spread a string into an object πŸ™€

const characters = {..."apple"}

console.log(characters)

// {
//   0: 'a',
//   1: 'p',
//   2: 'p',
//   3: 'l',
//   4: 'e'
// }

Stay safe out there kids.


Know any other weird or wonderful uses for the JS spread operator? Let me know in the comments 😊

Posted on by:

harrison_codes profile

Harrison Reid

@harrison_codes

My side projects haunt my dreams.

Discussion

markdown guide
 

As spread operator acts as if we used String[Symbol.iterator], it is better to use spread operator instead of regular String.prototype.split to split given string into characters when the string may involve Unicode characters. It's not foolproof, but better.
For example,

"πŸ˜‚πŸ‘»".split("") // (4)Β ["οΏ½", "οΏ½", "οΏ½", "οΏ½"] <- wut?
[..."πŸ˜‚πŸ‘»"] //  2)Β ["πŸ˜‚", "πŸ‘»"]

So, if you get asked to reverse a string with JS in an interview, following might be treated as too naive:

function reverse(str) {
  return str.split('').reverse().join('');
}
reverse('foo') // "oof"
reverse('π„ž') // "οΏ½οΏ½"

Following is slightly better:

function reverse(str) {
  return [...str].reverse().join('');
}
reverse('foo') // "oof"
reverse('π„ž') // "π„ž"
 

You might think this should result in an error, but it actually doesn't.

JavaScript's unofficial motto.

 
 

Thanks for the post Harrison, but there is a part of me that would prefer an object which is missing a property, to instead contain something explicit, like null or better yet a failover. With that said, my way or this way we would still be checking downstream that something is not here OR is null or failover. But mine would not need gork time, maybe πŸ€·β€β™‚οΈ (I'd do something else wierd instead)

 

Absolutely - I tend to agree that while there are some cases where this pattern is useful, it’s often better to keep a consistent object schema, and explicitly set the missing properties to null.

As with all things (especially somewhat obscure patterns), use with caution!

 

It's a hobby to know strange JavaScript, I can relate.

I am also interested in the obscure, checkout my labeled loops post, it's right up there in the do not do this camp.

 

Hi Harrison.

It is very nice article which highlights some of the unknown uses of spread operator. Thanks for this.

I think there is a typo in the code at 'const fruitsObject = { ...fruitsObject }'. Should it be const fruitsObject = { ...fruitsArray } ? Please check.

Thanks again
Vijay

 

Right you are! Thanks for picking up on that, I’ve updated the post 😊.

Thanks for reading!

 

Amazing article, thank you!

Personally, with the first example, I would also recommend using parens to help understand the order of operations.

const userInfo = {
  ...(firstName && { firstName }),
  ...(lastName && { lastName }),
  ...(address && { address }),
  ...(phoneNumber && { phoneNumber })
}

Otherwise looking at ...firstName && { firstName }, was throwing me off thinking firstName was first being spread!

 

String spreading is on fire! Thank you for sharing these interestings facts about this operator :)

 

Thanks for reading! 😊

 

Thanks for sharing the article Harrison!
One small comment: in your last example the object should start with the key zero for the character 'a' instead of one.

 

Good catch! Thanks for pointing that out - I’ve updated the post 🍻

 

Thanks for updating, and thanks for just writing this! I didn't know about the first pattern πŸ™

 

spreading a string into an array sounds like it could be a good approach to set text sarcastically and then duct tape the array back into a string again.