javascript typescript spread operator
| |

6 Awesome Tricks with the Spread and Rest Operators in Typescript and Javascript Objects

Introduction

You may have seen this syntax in Typescript and Javascript Projects before:

const {id, name, age, ...props} = obj

if you have used React, then you have very likely encountered this syntax a couple of times when passing the props from one component to another.

The support for the spread and rest operators was added since:

Typescript 2.1 and onwards

Javascript with ES6 specifications and onwards

As you are reading this article, the support for those operators has been there for a very long time, and whatever environment you’re using pretty much supports it now.

The spread and rest operators have a number of uses that can make manipulating an object a joy, and here are 6 ways to do so!

1 Clone an Object with Spread Operator

There will be times when you want to modify an object, but without losing the original object data.

The problem

const cat = {
  name: "chase",
  type: "cat",
  color: "black",
  age: 2,
  favoriteFood: "fish"
};

const anotherCat = cat;// ❌ this will only create a reference of "cat"

anotherCat.age = 5;

console.log(cat);
referenced image
output

Doing this will only create a reference of our cat object, meaning they are one and the same in memory. Modifying either of them will update the same memory allocation.

What if we wanted to modify anotherCat without cat being affected?

Spread operator to the rescue!

with the spread operator, we can quickly create a clone of our object!

const cat = {
  name: "chase",
  type: "cat",
  color: "black",
  age: 2,
  favoriteFood: "fish"
};

const copiedCat = { ...cat }; // âś… this will create a clone (new instance) of our variable

copiedCat.age = 5; // and we can change its values without affecting the original variable

console.log("original cat", cat);
console.log("copied cat", copiedCat);
spread operator, different result
output

2 Extract Values from an object

This one isn’t directly about the spread and rest operators, but it will build the knowledge required for our next point!

We can extract specific properties from our object by doing so:

const cat = {
  name: "chase",
  type: "cat",
  color: "black",
  age: 2,
  favoriteFood: "fish"
};

const { name, age } = cat;

console.log("my cat's name is " + name);
output

what you have to take note of when extracting properties is we are creating new instances for everything. This means if we modify cat after extracting its properties, then the changes won’t be reflected in the extracted properties.

const cat = {
  name: "chase",
  type: "cat",
  color: "black",
  age: 2,
  favoriteFood: "fish"
};

const { name, age } = cat;

cat.name = "luna"; // value assignment after extracting the properties

console.log("my cat's name is " + name);
output

So, if we wanted to have this change apply to our extracted properties, we have to apply them before extracting the properties like so:

const cat = {
  name: "chase",
  type: "cat",
  color: "black",
  age: 2,
  favoriteFood: "fish"
};
cat.name = "luna"; // value assignment before extracting the properties

const { name, age } = cat;

console.log("my cat's name is " + name);
output

3 Exclude Values from an object with Rest Operator

This is a cool one. What if we wanted to remove some of the properties?

this is where the reset operator comes in

const cat = {
  name: "chase",
  type: "cat",
  color: "black",
  age: 2,
  favoriteFood: "fish"
};

const { favoriteFood, age, ...newCat } = cat;

console.log(newCat);

the rest operator here means “combine the rest of the properties in a new object”. and it will give us this result:

output

and just like extracting properties, modifying cat properties will not update the values for newCat, as it is a completely new object:

const cat = {
  name: "chase",
  type: "cat",
  color: "black",
  age: 2,
  favoriteFood: "fish"
};

const { favoriteFood, age, ...newCat } = cat;
cat.name = "luna"; // this will not update newCat name
console.log(newCat);

Spread Operator vs Rest Operator in Typescript and Javascript

The Spread Operator

The spread operator is found on the right-hand side of the object assignment.

It spreads the object keys in the sense that it iterates over each property:

const cat = {
  name: "chase",
  type: "cat",
  color: "black",
};

const anotherCat = {...cat}
                    // 👆 here, we are using the spread operator.
                    // what we are doing here is iterating over each key:
                    // name:"chase", type:"cat", color:"black"

This is what we are doing if we wanted to look at it in a “literal” sense:

const cat = {
  name: "chase",
  type: "cat",
  color: "black",
};

const anotherCat = {name:"chase", type:"cat", black:"black"}

now It makes more sense how the spread operator is creating a new instance of the object.

The Rest Operator

The rest operator is found on the left-hand side of the object assignment.

with the rest operator, we are doing the opposite. we are collecting all of the properties in a single object.

const cat = {
  name: "chase",
  type: "cat",
  color: "black"
};

const { type } = cat; // we extracted type, but we can still extract "name" and "color"

now if we wanted to combine the remaining name and color properties, then we will use the rest operator like in our previous example.

const cat = {
  name: "chase",
  type: "cat",
  color: "black"
};

const { type, ...newCat } = cat;

and a “literal” sense, this is what we are doing:

const cat = {
  name: "chase",
  type: "cat",
  color: "black"
};

const { type } = cat;
const newCat = { name: "chase", color: "black" };

So from those examples, I believe you can understand that we are using the spread and rest operators to clone properties and their values from an existing object, rather than writing them down manually.

Okay, but what happens if we use the rest operator without extracting any properties?

Bonus: Another way to clone an object

Now that we are aware of what the rest operator is doing, we can also come to this conclusion:

const cat = {
  name: "chase",
  type: "cat",
  color: "black",
  age: 2,
  favoriteFood: "fish"
};

const catCopy = { ...cat }; // this creates a clone by using spread operator!
const { ...anotherCopy } = cat; // this also creates a clone by using rest operator!

catCopy.name = "lulu";
anotherCopy.name = "zoey";

console.log(cat.name, catCopy.name, anotherCopy.name);
output

Both syntaxes work to achieve the same goal to clone an object

4 Combine objects with Spread Operator

let us say that we have an object that we want to change some of its properties, we can use the spread operator to do so:

const cat = {
  name: "chase",
  type: "cat",
  color: "black",
  age: 2,
  favoriteFood: "fish"
};

const anotherCat = {
  name: "luna",
  type: "bengal",
  age: 5
};

const combinedCat = { ...cat, ...anotherCat };

console.log(combinedCat);
output

as you can see, we have changed the name, type, and age properties of the cat object with the ones that were provided by anotherCat.

A few things to know:

  • Once again, we are creating a new object, and not referencing either of the original objects.
  • The last object’s values are the ones that will be put in the combined object.

Combine Multiple Objects

To elaborate more on the second point, this means that if we were to have a third object in there, then its values are the ones that will apply in the combined object.

const cat = {
  name: "chase",
  type: "cat",
  color: "black",
  age: 2,
  favoriteFood: "fish"
};

const anotherCat = {
  name: "luna",
  type: "bengal",
  age: 5
};

const thirdCat = {
  name: "cello"
};

const combinedCat = { ...cat, ...anotherCat, ...thirdCat };

console.log(combinedCat);

Think of it like this:

anotherCat values will be applied on top of cat

then, thirdCat values will be applied on top of the modified cat

Combine objects with different properties

combining with the spread operator does not restrict us to having the exact same properties of the first object.

const cat = {
  name: "chase",
  type: "cat",
  color: "black",
  age: 2,
  favoriteFood: "fish"
};

const anotherCat = {
  name: "luna",
  type: "bengal",
  age: 5,
  favoriteToy: "snuggles",
  favoriteSpot: "my couch"
};

const combinedCat = { ...cat, ...anotherCat };

console.log(combinedCat);
output

this way, the properties that didn’t exist with cat will be added in the combinedCat object!

5 Change Multiple Properties

const cat = {
  name: "chase",
  type: "cat",
  color: "black",
  age: 2,
  favoriteFood: "fish"
};

const newCat = { ...cat, name: "cello", age: 6, type: "sphynx" };

console.log(newCat)
output

we can also combine this with our previous example!

const cat = {
  name: "chase",
  type: "cat",
  color: "black",
  age: 2,
  favoriteFood: "fish"
};

const secondCat = {
  name: "chase",
  type: "cat",
  color: "black",
  age: 2,
  favoriteFood: "wet food"
};

const newCat = {
  ...cat,
  ...secondCat,
  name: "cello",
  age: 6,
  type: "sphynx",
  isHappy: true
};

console.log(newCat);
output

6 Add new properties to an object with Spread Operator

If we wanted to add a property that didn’t exist in the object before:

const cat = {
  name: "chase",
  type: "cat",
  color: "black",
  age: 2,
  favoriteFood: "fish"
};

cat.isHappy = true; // ❌ this will **NOT** work

Then we will get the following error:

Property 'isHappy' does not exist on type '{ name: string; type: string; color: string; age: number; favoriteFood: string; }'.ts(2339)
ts error

to work around this issue, we can use the spread operator to create a new object with the new property!

const cat = {
  name: "chase",
  type: "cat",
  color: "black",
  age: 2,
  favoriteFood: "fish"
};

const modifiedCat = { ...cat, isHappy: true };

console.log(modifiedCat);
output

To really understand this in Typescript, if we were to make an interface, we can see that the new object generated by the spread operator does NOT have the original interface:

interface Cat {
  name: string;
  type: string;
  color: string;
  age: number;
  favoriteFood: string;
}

const cat: Cat = {
  name: "chase",
  type: "cat",
  color: "black",
  age: 2,
  favoriteFood: "fish"
};

const newCat = { ...cat };
output

this means we are creating a new object without the constraints of our Cat interface.

so if we wanted to retain the type safety of our cloned object, then we will need to explicitly say that it is of type Cat.

interface Cat {
  name: string;
  type: string;
  color: string;
  age: number;
  favoriteFood: string;
}

const cat: Cat = {
  name: "chase",
  type: "cat",
  color: "black",
  age: 2,
  favoriteFood: "fish"
};

const newCat:Cat = { ...cat, isHappy:true }; // however, this will no longer work.
output

and if we really wanted everything to have a proper interface for it, then we can do something like this:

interface Cat {
  name: string;
  type: string;
  color: string;
  age: number;
  favoriteFood: string;
}

interface HappyCat extends Cat {
  isHappy: boolean;
}

const cat: Cat = {
  name: "chase",
  type: "cat",
  color: "black",
  age: 2,
  favoriteFood: "fish"
};

const newCat: HappyCat = { ...cat, isHappy: true }; //  this will work!

Bonus: everything together!

Let us recap by using everything we just learned!

this is a more advanced example where we’ll be using multiple things together:

const cat = {
  name: "chase",
  type: "cat",
  color: "black",
  age: 2,
  favoriteFood: "fish"
};

const updateCatObject = {
  name: "chase",
  type: "cat",
  color: "white",
  age: 4,
  favoriteFood: "wet catfood"
};

// rest operator to combine the remaining properties
const { name, type, ...updateCat } = updateCatObject;

const catWithNewProp = { ...updateCat, favoriteSpot: "sofa" };

const combinedCat = {
  ...cat, // spread operator, spreading the name, type, color, age and favoriteFood properies
  ...catWithNewProp, //spread operator, spreading the color, age, favoriteFood, favoriteSpot
  name: "lulu", // modifing an existing property
  lovesLasers: true // adding a new property
};

const newCat = { ...combinedCat }; // spreading our entire combined object

newCat.name = "cello";

console.log(newCat);
console.log(combinedCat);

before you have a look at the output, I’d want you to have a moment to think about what the result of newCat and combinedCat will be. See if you can get it right.

Conclusion

The spread and rest operators are very powerful tools to manipulate objects. You will very likely be using it in your Typescript and Javascript apps. So make sure you get comfortable with it! You will likely use them very often in your react and express apps.

Sing up to get an email notification when new content is published

Subscription Form

Leave a Reply

Your email address will not be published. Required fields are marked *

Similar Posts