【JS / TS】分割代入のレスト構文ってなに？（スプレット構文とは違うよ編）

# 1.はじめに
分割代入の第4段の記事になります。

今回は、分割代入のレスト構文編の記事となります。

 [オブジェクト編](https://qiita.com/daishiman/items/5acf803f252b82f970fe) 、[配列編](https://qiita.com/daishiman/items/324dcc0eae72eb6b13cf)、[複雑な分割代入編](https://qiita.com/daishiman/items/d382279a2a6cae3ea1aa) と分割代入の記事を書いてきましたが、  
 本記事は、配列編と複雑な分割代入編の間の位置づけになります。

間違って解釈している所ありましたら、ご指摘いただけますと幸いです。

レスト構文に似た [スプレッド構文](https://qiita.com/daishiman/items/42ba60a223cadbd98104) の記事も書いていますので、
他の記事も見て頂けると、うれしいです。


# 2.目次
[1. はじめに](#1はじめに)  
[2. 目次](#2目次)  
[3. この記事でわかること](#3この記事でわかること)  
[4. この記事で取り扱わないこと](#4この記事で取り扱わないこと)  
[5. 環境](#5環境)  
[6. レスト構文の分割代入](#6レスト構文の分割代入)  
　[6.1. レスト構文とは？](#61レスト構文とは)  
　[6.2. レスト構文の使いかた](#62レスト構文の使いかた)  
　[6.3. オブジェクトや配列は、コピーされる](#63オブジェクトや配列はコピーされる)  
　[6.4. 元のオブジェクトや配列と同じ名前は使えない](#64元のオブジェクトや配列と同じ名前は使えない)  
　[6.5. レスト構文は、最後にしか使えない](#65レスト構文は最後にしか使えない)  
　[6.6. レスト構文は、複数使えない](#66レスト構文は複数使えない)  
　[6.7. ネストしたときのレスト構文は、コピーされない](#67ネストしたときのレスト構文はコピーされない)  
[7. まとめ](#7まとめ)  
[8. 参考](#8参考)  


# 3.この記事でわかること

:::note info

「分割代入の **レスト構文** わからん」を克服できます。

- レスト構文の分割代入
  - スプレッド構文とは違う
  - 元のオブジェクトや配列と **同じ名前は使えない**
  - レスト構文で作られたオブジェクトや配列は、**コピーされる**
  - レスト構文は、**最後にしか使えない**
  - **ネスト** したときのレスト構文は、**コピーされない**  
  - **ネスト** したときの分割代入は、**各ネスト毎** にレスト構文を使用できる

:::




# 4.この記事で取り扱わないこと

**レスト構文** と **スプレッド構文** の違いについては取り扱いませんので他の記事を参考にしていただけますと幸いです。

下記に参考記事のリンクを貼っておきます。

- [【JavaScript】「スプレッド構文」と「レスト構文」について](https://qiita.com/kamijodev/items/89543ca267ac9ad51529)
- [【JS】スプレッド構文とレスト構文の使い方、違いについて](https://jsnotice.com/posts/2020-07-22/)

あと、分割代入の詳細説明も、下記の記事にまとめましたので省略いたします。
- [オブジェクト編](https://qiita.com/daishiman/items/5acf803f252b82f970fe)
- [配列編](https://qiita.com/daishiman/items/324dcc0eae72eb6b13cf)
- [複雑な分割代入編](https://qiita.com/daishiman/items/d382279a2a6cae3ea1aa) 


# 5.環境

- TypeScript: 4.7.4
- Node.js: 16.15.1




# 6.レスト構文の分割代入

## 6.1.レスト構文とは？

スプレッド演算子の `...` を用いて `...変数名` と記述し、オブジェクトや配列の **残りものを１つにまとめる** 特徴を持っています。 


似たものとして、スプレッド構文というものがあります。  
こちらも、`...` と記述します。


([この記事で取り扱わないこと](#この記事で取り扱わないこと) でも書きましたが、  
本記事では **レスト構文とスプレッド構文の違い** の詳細説明は省略させていただきます。）  

ざっくりいうと、  

:::note info
レスト構文とスプレッド構文の違い

**レスト構文** は、「残りものを１つに**まとめる**」  
**スプレッド構文** は「**展開** する」  

:::

です。

この認識を持っていただくと、  
「あれ？ これは、レスト構文？スプレッド構文？」  
っていうのを防げるかと思います。

スプレッド構文についてまとめた記事を以前書いたので、  
「スプレッド構文とは？」  
となった人は、こちらの記事を読んで頂けると解るかと思います。

https://qiita.com/daishiman/items/42ba60a223cadbd98104



## 6.2.レスト構文の使いかた

簡単なオブジェクトと配列を用いて、レスト構文の使いかたを見ていきます。

分割代入の中で `...変数名` と記述します。

こうすることで、複数のキーや値を1つの変数に **まとめて** 代入することができます。

詳細は下記を御覧ください。


### 6.2.1.オブジェクトのとき

```ts:オブジェクトの分割代入のレスト構文（使いかた）
const manjuInfo = {
	nickname: 'manju',
	age: 35,
	email: 'manju@example.com',
	sex: 'male',
};

// レスト構文を用いた分割代入
const { nickname, ...info } = manjuInfo;

console.log(nickname); // manju
console.log(info);     // { age: 35, email: 'manju@example.com', sex: 'male' }
```

分割代入の `nickname` は、

```ts:分割代入を使わずに記述
const nickname = manjuInfo.nickname;
```

を意味しています（詳細は、[こちら](https://qiita.com/daishiman/items/5acf803f252b82f970fe) の記事参照）。

`...info` の部分が、レスト構文の分割代入となります。

[レスト構文とは？](#61レスト構文とは) でも記述したとおり、  
`nickname` プロパティ以外の **残り** のプロパティを変数 `info` に**まとめ** ます。

意味合い的には、

```ts:レスト構文を分割代入を使わずに記述
const info = {
  manjuInfo.age,
  manjuInfo.email,
  manjuInfo.sex,
};
```

となります。

このように、

:::note info

レスト構文の分割代入を使用することで、1つのオブジェクトに値を **まとめることができる**

:::

### 6.2.2.配列のとき

```ts:配列の分割代入のレスト構文（使いかた）
const daishiSkills = ['JavaScript', 'TypeScript', 'PHP', 'Ruby', 'AWS'];

// レスト構文を用いた分割代入
const [firstSkill, ...otherSkills] = daishiSkills;

console.log(firstSkill);  // JavaScript
console.log(otherSkills); // [ 'TypeScript', 'PHP', 'Ruby’, ‘AWS’ ]
```

配列のレスト構文を用いた分割代入もオブジェクトと同じように使います。

分割代入の `firstSkill` は、  

```ts:分割代入を使わずに記述
  const firstSkill = daishiSkills[0];
```

を意味しています（詳細は、[こちら](https://qiita.com/daishiman/items/5acf803f252b82f970fe) の記事参照）。

`...otherSkills` の部分が、レスト構文の分割代入となります。

`0` 要素以外の要素を変数 `otherSkills` に**まとめ** ます。

意味合い的には、

```ts:レスト構文を分割代入を使わずに記述
const otherSkills = [
  daishiSkills.[1],
  daishiSkills.[2],
  daishiSkills.[3],
  daishiSkills.[4],
];
```

となります。

このように `otherSkills` 変数に配列の **残りの要素をまとめ** ています。


:::note info

レスト構文の分割代入を使用することで、1つの配列に値を **まとめることができる**

:::

## 6.3.オブジェクトや配列は、コピーされる

分割代入で作成さえれたオブジェクトや配列は、コピーされます。  

同様に、**レスト構文を使用した分割代入もコピーされます** 。  

ただし、ネストしたオブジェクトや配列を除きます。  
ネストしたものに関しては、[こちら](#67ネストしたときのレスト構文はコピーされない) にて説明しております。

ここでは、コピーとはどういうことかを説明します。


### 6.3.1.オブジェクトのとき

```ts:オブジェクトの分割代入のレスト構文（コピー）
const manjuInfo = {
    nickname: 'manju',
    age: 35,
    email: 'manju@example.com',
    sex: 'male',
};

// レスト構文を用いた分割代入
const { nickname, ...info } = manjuInfo;

// コピー元の manjuInfo の nickname, email に再代入
manjuInfo.nickname = 'daishi';
manjuInfo.email = 'daishi@example.com';

console.log(manjuInfo); // {nickname: 'daishi', age: 35, email: 'daishi@example.com', sex: 'male' }
console.log(nickname);  // manju
console.log(info);      // { age: 35, email: 'manju@example.com', sex: 'male' }
```

`manjuInfo` オブジェクトの `nickname`、 `email` プロパティに再代入しました。

元 の `manjuInfo` は、

```ts:
const manjuInfo = 
{
    nickname: 'manju',
    age: 35,
    email: 'manju@example.com',
    sex: 'male',
};
```

から

```ts:
const manjuInfo = 
{
    nickname: 'daishi',
    age: 35, 
    email: 'daishi@example.com',
    sex: 'male'
};
```

に変化しましたが、

分割代入で作成した `niciname`、 `info` は、

```ts:
const nickname = manju;

const info = 
{
    age: 35,
    email: 'manju@example.com', 
    sex: 'male'
};
```

と変化していません。

これが **コピーする** ということです。

:::note info

分割代入で作成されたオブジェクトは、**元の配列の変更が反映されない**

:::





### 6.3.2.配列のとき

```ts:配列の分割代入のレスト構文（コピー）
const daishiSkills = ['JavaScript', 'TypeScript', 'PHP', 'Ruby', 'AWS'];

// レスト構文を用いた分割代入
const [firstSkill, ...otherSkills] = daishiSkills;

// コピー元の daishiSkills の 0, 2 要素目に再代入
daishiSkills[0] = 'React';
daishiSkills[2] = 'Java';

console.log(daishiSkills); // [ 'React', 'TypeScript', 'Java', 'Ruby', 'AWS' ]
console.log(firstSkill);   // JavaScript
console.log(otherSkills);  // [ 'TypeScript', 'PHP', 'Ruby’, ‘AWS’ ]
```

配列もオブジェクト同様にコピーとなります。  

`daishiSkills` の  `0`, `2` 要素目に再代入しました。

元 の `daishiSkills` は、

```ts:
const daishiSkills = ['JavaScript', 'TypeScript', 'PHP', 'Ruby', 'AWS'];
```

から

```ts:
const daishiSkills = ['React', 'TypeScript', 'Java', 'Ruby', 'AWS']
```

に変化しましたが、

分割代入で作成した `firstSkill`、 `otherSkills` は、

```ts:
const firstSkill = JavaScript;

const otherSkills = ['TypeScript', 'PHP', 'Ruby’, ‘AWS’];
```

と変化していません。


:::note info

分割代入で作成された配列は、**元の配列の変更が反映されない**

:::





## 6.4.元のオブジェクトや配列と同じ名前は使えない

レスト構文を使用しない分割代入は、  
プロパティのキーと同じ変数名を使用する必要がありました（詳細は[こちら](https://qiita.com/daishiman/items/5acf803f252b82f970fe#512%E5%88%86%E5%89%B2%E4%BB%A3%E5%85%A5%E3%81%A7%E8%A8%98%E8%BF%B0) の記事参照）。  

レスト構文の変数名には、**元のオブジェクト名や配列名と同じ名前を使用できません**。

同じ名前をしようできないのは、どういことかを説明します。


### 6.4.1.オブジェクトのとき

```ts:オブジェクトの分割代入のレスト構文（変数名）
const manjuInfo = {
	nickname: 'manju',
	age: 35,
	email: 'manju@example.com',
	sex: 'male',
};

// 元のオブジェクト名と同じ名前のレスト構文
const { ...manjuInfo } = manjuInfo;
```

分割代入に元のオブジェクトの変数名と同じ変数名 `manjuInfo` を定義しました。

コンパイルしてみると、エラーがでます。

:::note alert
エラー内容

Cannot redeclare block-scoped variable 'manjuInfo'

:::

すでに、`manjuInfo` は宣言しているから **再宣言** できません。

:::note info

元のオブジェクトの変数名と **同じ変数名** は、レスト構文の分割代入に使えない

:::





### 6.4.2.配列のとき

```ts:配列の分割代入のレスト構文（変数名）
const daishiSkills = ['JavaScript', 'TypeScript', 'PHP', 'Ruby', 'AWS'];

// 元の配列名と同じ名前のレスト構文
const [...daishiSkills] = daishiSkills;
```

分割代入に元の配列の変数名と同じ変数名 `daishiSkills` を定義しました。

コンパイルしてみると、エラーがでます。

:::note alert
エラー内容

Cannot redeclare block-scoped variable 'daishiSkills'

:::

すでに、`daishiSkills` は宣言しているから **再宣言** できません。

:::note info

元の配列の変数名と **同じ変数名** は、レスト構文の分割代入に使えない

:::




## 6.5.レスト構文は、最後にしか使えない

分割代入でレスト構文での変数定義は、**最後にしか** できません。

途中で使用するとエラーとなります。

変数定義の順番には注意が必要になります。

では、こちらもどういうことか、見ていきましょう。


### 6.5.1.オブジェクトのとき

```ts:オブジェクトの分割代入のレスト構文（配置）
const manjuInfo = {
    nickname: 'manju',
    age: 35,
    email: 'manju@example.com',
    sex: 'male',
};

// レスト構文を分割代入の途中に入れる
const { nickname, ...info, sex } = manjuInfo;
```

分割代入の途中にレスト構文 `...info` を入れました。

コンパイルしてみると、エラーがでます。

:::note alert
エラー内容

 A rest element must be last in a destructuring pattern.

:::

要約すると、  
「分割代入でレスト構文を使うときは、最後にないとダメですよ」  
と言っていますね

:::note info

レスト構文の分割代入は、最後にしか使えない

:::

レスト構文の **残りもの** をひとつにまとめるということを違反していますね。



### 6.5.2.配列のとき

```ts:配列の分割代入のレスト構文（配置）
const daishiSkills = ['JavaScript', 'TypeScript', 'PHP', 'Ruby', 'AWS'];

// レスト構文を用いた分割代入
const [firstSkill, ...otherSkills, infra] = daishiSkills;
```

分割代入の途中にレスト構文 `...otherSkills` を入れました。

こちらもコンパイルすると、エラーがでます。

:::note alert
エラー内容

 A rest element must be last in a destructuring pattern.

:::

オブジェクトのとき同様に  
「分割代入でレスト構文を使うときは、最後にないとダメですよ」  
と言っていますね。

つまり、

:::note info

レスト構文の分割代入は、最後にしか使えない

:::




## 6.6.レスト構文は、複数使えない

レスト構文は、複数使えません。

複数使用できないないのは、上記の [レスト構文は、最後にしか使えない](#65レスト構文は最後にしか使えない) でも記述した通り、

 **残りもの** をひとつにまとめるということを違反しているからです。

では、こちらもどういうことか、下記を御覧ください。




### 6.6.1.オブジェクトのとき

```ts:オブジェクトの分割代入のレスト構文（複数の使用）
const manjuInfo = {
    nickname: 'manju',
    age: 35,
    email: 'manju@example.com',
    sex: 'male',
};

// 複数のレスト構文を用いた分割代入
const { ...publicInfo, ...privateInfo } = manjuInfo
```

分割代入の中に、レスト構文を２つ `...publicInfo`、`...privateInfo` 記述しました。

こちらも、エラーがでます。

:::note alert
エラー内容

 A rest element must be last in a destructuring pattern.

:::

[レスト構文は、最後にしか使えない](#65レスト構文は最後にしか使えない) ときと同じエラーが出ました。

「分割代入でレスト構文を使うときは、最後にないとダメですよ」  
と言っていますね。

分割代入の途中に分割代入を用いて、最後にも分割代入を用いていいるから同じエラーが出ます。

つまり、

:::note info

レスト構文の分割代入は、複数使用できない

:::

ということを言っています。




### 6.6.2.配列のとき

```ts:配列の分割代入のレスト構文（複数の使用）
const daishiSkills = ['JavaScript', 'TypeScript', 'PHP', 'Ruby'];

// 複数のレスト構文を用いた分割代入
const [...frontSkills, ...backSkills] = daishiSkills;
```

分割代入の中に、レスト構文を２つ `...frontSkills`、`...backSkills` 記述しました。

こちらもやはり、エラーがでます。

:::note alert
エラー内容

 A rest element must be last in a destructuring pattern.

:::

こちらも、同様のエラーですね。

つまり、配列も

:::note info

レスト構文の分割代入は、複数使用できない

:::

ということです。



## 6.7.ネストしたときのレスト構文は、コピーされない

ネストしたオブジェクトや配列はコピーできません。  

ネストしたオブジェクトもコピーするためには、  

1. ライブラリを用いる
2. ネストしたオブジェクトにレスト構文を用いる

この2バターンあるかと思います。

ここでは、2. の「ネストしたオブジェクトにレスト構文を用いる」方法を見ていきます。

ライブラリを用いた方が便利ですが、  
**ネストしたオブジェクトがどのような動きをするのか** 見ていきたいので 2. を選択しました。

 1.のライブラリとして下記のものを使用されているようです。  
まだ本格的に使用できていないので、紹介だけになりますm(_ _)m

https://github.com/angus-c/just

https://flaviocopes.com/how-to-clone-javascript-object/




### 6.7.1.オブジェクトのとき

```ts:オブジェクトの分割代入のレスト構文（ネスト）
const manjuInfo = {
    publicInfo: {
        nickname: 'manju',
        age: 35,
    },
    privateInfo: {
        email: 'manju@example.com',
        sex: 'male',
    },
};

// レスト構文を用いた分割代入
const {...whoAreYou} = manjuInfo;

// ネストしたオブジェクトのレスト構文を用いた分割代入
manjuInfo.publicInfo.nickname = 'daishi';
manjuInfo.privateInfo.email = 'daishi@example.com';

console.log(manjuInfo); // { publicInfo: { nickname: 'daishi', age: 35 }, privateInfo: { email: 'daishi@example.com', sex: 'male' } }
console.log(whoAreYou); // { publicInfo: { nickname: 'daishi', age: 35 }, privateInfo: { email: 'daishi@example.com', sex: 'male' } }

```


レスト構文の分割代入で `whoAreYou` オブジェクトを作成した後に、  
`manjuInfo` の `publickInfo.nickname` プロパティと `priveteInfo.email` プロパティに再代入しました。

再代入前の `manjuInfo` をコピーしてるはずなので、  
`whoAreYou` は、

```ts:
{
    publicInfo: {
        nickname: 'manju',
        age: 35,
    },
    privateInfo: {
        email: 'manju@example.com',
        sex: 'male',
    },
};
```

となってほしいところ、  

```ts:
{ 
    publicInfo: { 
        nickname: 'daishi', // ← 意図しない変更箇所
        age: 35
    },
    privateInfo: { 
        email: 'daishi@example.com', // ← 意図しない変更箇所
        sex: 'male'
    },
};
```

となっています。

`publicInf.nickname`、`privateInfo.email` プロパティ共に再代入 **後** の値が入っています。 

このことから、

:::note warn

**ネストしたオブジェクトはコピーされない** 

:::

ことがわかります。

ネストしたオブジェクトをコピーするためには、  
ネストしたオブジェクトの部分でレスト構文を使用する必要があります。



```ts:オブジェクトの分割代入のレスト構文（ネストもコピー）
const manjuInfo = {
    publicInfo: {
        nickname: 'manju',
        age: 35,
    },
    privateInfo: {
        email: 'manju@example.com',
        sex: 'male',
    },
};

// ネストしたオブジェクトのレスト構文を用いた分割代入
const {
    publicInfo: {...publicInfo},
    privateInfo: {...privateInfo},
} = manjuInfo;

// nickname と email プロパティに再代入
manjuInfo.publicInfo.nickname = 'daishi';
manjuInfo.privateInfo.email = 'daishi@example.com';

console.log(manjuInfo);   // { nickname: 'daishi', age: 35, privateInfo: { email: 'daishi@example.com', sex: 'male' } }
console.log(publickInfo); // { nickname: 'manju', age: 35 }
console.log(privateInfo); // { email: 'manju@example.com', sex: 'male' }
```

ネストした部分もレスト構文を使用すると、  
コピー元に再代入しても、**コピー先は変わっていない** のがわかるかと思います。

:::note info

ネストしたオブジェクトをコピーする際は、ネスト部分もレスト構文を使用する

:::

ここで、
ネストしたオブジェクトのレスト構文を用いた分割代入を見てみると、

:::note warn

「分割代入では、複数のレスト構文使えへんって言ったやん！？」  
「途中には、使えへんって言ったやん！？」

使えとるやん！！

:::

って疑問が生まれたかと思います。

```ts:
const {
    publicInfo: {...publicInfo},
    privateInfo: {...privateInfo},
} = manjuInfo;
```


これは、ネストしたオブジェクトの中身と1つにまとめているので使えるという解釈をしています。

↑ 少し曖昧な解釈となっており、申し訳ありません。  
ご指摘やアドバイス頂けますと幸いです。


### 6.7.2.配列のとき

```ts:配列の分割代入のレスト構文（ネスト）
const daishiSkills = [
  ['JavaScript', 'TypeScript'],
  ['PHP', 'Ruby'], ['AWS']
];

// ネストした配列のレスト構文を用いた分割代入
const [...whatSkills] = daishiSkills;

//'JavaScript'と 'Ruby' の値を再代入で変更
daishiSkills[0][0] = 'React';
daishiSkills[1][1] = 'Java';

console.log(daishiSkills); // [ [ 'React', 'TypeScript' ], [ 'PHP', 'Java' ], [ 'AWS' ] ]
console.log(whatSkills);   // [ [ 'React', 'TypeScript' ], [ 'PHP', 'Java' ], [ 'AWS' ] ]
```

レスト構文の分割代入で `whatSkills` の配列を作成した後に、  
`daishiSkills` の `JavaScript` と `'Ruby` の `email` プロパティに再代入しました。

オブジェクトのときと挙動が似ているので説明は省略しますが、

変更しないで欲しいはずの、分割代入の `whatSkills` の値が変化しています。

ネスト部分もコピーするためには、  
ネスト部分もレスト構文で使用する必要があります。


```ts:配列の分割代入のレスト構文（ネストもコピー）
const daishiSkills = [['JavaScript', 'TypeScript'], ['PHP', 'Ruby'], ['AWS']];

// ネストした配列のレスト構文を用いた分割代入
const [
  [...frontSkills],
  [...backSkills],
  [...infraSkills]
] = daishiSkills;

//'JavaScript'と 'Ruby' の値を再代入で変更
daishiSkills[0][0] = 'React';
daishiSkills[1][1] = 'Java';

console.log(daishiSkills); // [ [ 'React', 'TypeScript' ], [ 'PHP', 'Java' ], [ 'AWS' ] ]
console.log(frontSkills);  // [ 'JavaScript', 'TypeScript' ]
console.log(backSkills);   // [ 'PHP', 'Ruby' ]
console.log(infraSkills);  // [ 'AWS' ]
```

ネスト部分をレスト構文で変数定義すると、コピーできているのがわかるかと思います。

※ オブジェクトと同様に、ネストした分割代入の場合だと、複数のレスト構文を使用できます。


# 7.まとめ

レスト構文とスプレッド構文が、似ているので理解するのに苦戦しました。

さらに、分割代入も絡んでくるので 😱😱😱  状態でした。

分割代入、レスト構文、スプレッド構文は非常に便利で多岐にわたって使われているので、  
確実に理解して次のステップに進みたいなと思います。

併せて他の記事も読んでいただけると嬉しいです🙇‍♂️

https://qiita.com/daishiman/items/5acf803f252b82f970fe

https://qiita.com/daishiman/items/324dcc0eae72eb6b13cf

https://qiita.com/daishiman/items/d382279a2a6cae3ea1aa

https://qiita.com/daishiman/items/42ba60a223cadbd98104


長い記事となりましたが、最後まで読んでいただきありがとうございました。

# 8.参考

- [書籍：プロを目指す人のためのTypeScript入門　鈴木僚太［著］](https://twitter.com/uhyo_/status/1517158176365641729?s=20&t=5jZBSZVbLrAsgkCUruO7Ig) @uhyo
- [MDN Web Docs](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Spread_syntax)
- [【JavaScript】「スプレッド構文」と「レスト構文」について](https://qiita.com/kamijodev/items/89543ca267ac9ad51529)
- [【JS】スプレッド構文とレスト構文の使い方、違いについて](https://jsnotice.com/posts/2020-07-22/)