The Secret to the ES6 Spread Syntax

Writing more elegant JavaScript applications with ES6’s spread operator.

Joshua Tal
JavaScript in Plain English

--

The spread syntax has grown increasingly popular when it comes to writing clean and elegant JavaScript applications. By enabling us to spread the body of an object unto another, we can more seamlessly write pure, immutable functions. As always, examples are the best way to learn so let’s look at one here. Suppose we are writing some social media application that contains a preferences object based on the TypeScript interface:

interface Preferences { 
theme: "dark" | "light";
language: string;
shareLocation: boolean;
}

And we’re given the following default preferences:

const defaultPreferences = {
theme: "light",
language: "English",
shareLocation: false
}

Now consider that after pulling the logged-in user data from the API, it’s missing some preferences. Say we were received the following object from the API:

const apiResult = { language: "English" }

This might be for any reason like it wasn’t posted during account creation, it wasn’t initialized on the back-end, or maybe it’s a new feature. In any case, moving forward we want to avoid errors and henceforth fill in the missing values using our defaultPreferences object. How might we do that? After all, at some point, we’ll need to know if this user wants to share their location or not! Let’s explore a few approaches.

Using the ternary operator

We can use the ternary operator to check if the property is undefined like so:

const userPreferences = {
theme: apiResult.theme === undefined ? apiResult.theme : defaultPreferences.theme,
language: apiResult.language === undefined ? apiResult.language : defaultPreferences.language,
shareLocation: apiResult.shareLocation === undefined ? apiResult.shareLocation : defaultPreferences.shareLocation
}

That’ll work… if you hate your colleagues! In my opinion, while this solution does work, it’s difficult to read and repetitive. Let’s explore another option.

Using lodash “get”

Lodash is an awesome functional JavaScript library that I highly recommend. I constantly use methods from there such as curry, isNil, get, has, etc. Lodash’s get function accepts 3 parameters: an object, a string path to a property, and an optional alternative value to provide if the path returns undefined. Let’s look at how we can implement get in this scenario.

const userPreferences = {
theme: get(apiResult, "theme", defaultPreferences.theme),
language: get(apiResult, "language", defaultPreferences.language),
shareLocation: get(apiResult, "shareLocation", defaultPreferences.shareLocation)
}

While this approach is a tad cleaner than the first and will indeed prevent errors, it’s not my favorite. That’s because we’re basically writing the same thing over 3 times with a tiny change. People who know me as a developer know that I follow the DRY principle. If you intend to write readable code, I suggest you do too.

Using spread syntax

According to MDN documentation,

Spread syntax (...) allows an iterable such as an array expression or string to be expanded in places where zero or more arguments (for function calls) or elements (for array literals) are expected, or an object expression to be expanded in places where zero or more key-value pairs (for object literals) are expected.

In other words, you can literally spread out the object within another object and then continue to define whatever it is you were defining. Let’s look at some examples before implementing it as our final preferences solution.

We can append, combine, and create new objects:

//Given arrays
const abc = ['a', 'b', 'c']
const def = ['d', 'e', 'f']
//we can create
const abcdef = [...abc, ...def]
/* returns ['a', 'b', 'c', 'd', 'e', 'f'] */
//Given objects
const name = { first: 'josh', last: 'tal' }
const skillLevels = { javascript: 10, sports: 1, skateboarding: 5 }
//we can combine and contrive
const bio = { ...name, ...skillLevels }
/* returns { first: 'josh', last: 'tal', javascript: 10, sports: 1, skateboarding: 5 } */

We can also overwrite properties:

//Given objects
const lowerPriority = { moneyInBank: 5, collegeMajor: 'economics', favoriteFood: 'pizza' }
const higherPriority = { moneyInBank: 20, favoriteFood: 'hummus', favoriteAnime: 'one piece' }
const interests = ['keyboard', 'design', 'languages']
//we can combine and overwrite to create
const result = { ...lowerPriority, ...higherPriority, otherInterests: ['art', 'culture', 'scary pockets', ...interests] }
/* result returns...
{
moneyInBank: 20,
collegeMajor: 'economics',
favoriteFood: 'hummus',
favoriteAnime: 'one piece',
otherInterests: ['art', 'culture', 'scary pockets', 'keyboard', 'design', 'languages']
}
*/

Notice that the favoriteFood and moneyInBank of lowerPriority were overwritten. That’s because higherPriority takes precedence over it in the list as it’s written second. Also, notice how interests were appended to otherInterests due to the use of the spread syntax. You can perform lots of cool operations like these using the spread operator.

Now let’s see how we can use it to solve our problem:

const userPreferences = {
...defaultPreferences,
...apiResult
}

That’s it! We don’t even have to write the property names again. In fact, my policy is to try to avoid writing property names more than once unless absolutely necessary. That’s the power of ES6.

More content at plainenglish.io

--

--