14
6

More than 1 year has passed since last update.

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

Last updated at Posted at 2022-08-15

1.はじめに

分割代入の第4段の記事になります。

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

オブジェクト編配列編複雑な分割代入編 と分割代入の記事を書いてきましたが、
本記事は、配列編と複雑な分割代入編の間の位置づけになります。

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

レスト構文に似た スプレッド構文 の記事も書いていますので、
他の記事も見て頂けると、うれしいです。

2.目次

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

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

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

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

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

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

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

あと、分割代入の詳細説明も、下記の記事にまとめましたので省略いたします。

5.環境

  • TypeScript: 4.7.4
  • Node.js: 16.15.1

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

6.1.レスト構文とは?

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

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

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

ざっくりいうと、

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

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

です。

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

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

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

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

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

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

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

6.2.1.オブジェクトのとき

オブジェクトの分割代入のレスト構文(使いかた)
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 は、

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

を意味しています(詳細は、こちら の記事参照)。

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

レスト構文とは? でも記述したとおり、
nickname プロパティ以外の 残り のプロパティを変数 infoまとめ ます。

意味合い的には、

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

となります。

このように、

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

6.2.2.配列のとき

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

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

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

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

分割代入の firstSkill は、

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

を意味しています(詳細は、こちら の記事参照)。

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

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

意味合い的には、

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

となります。

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

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

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

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

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

ただし、ネストしたオブジェクトや配列を除きます。
ネストしたものに関しては、こちら にて説明しております。

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

6.3.1.オブジェクトのとき

オブジェクトの分割代入のレスト構文(コピー)
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 オブジェクトの nicknameemail プロパティに再代入しました。

元 の manjuInfo は、

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

から

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

に変化しましたが、

分割代入で作成した nicinameinfo は、

const nickname = manju;

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

と変化していません。

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

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

6.3.2.配列のとき

配列の分割代入のレスト構文(コピー)
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’ ]

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

daishiSkills0, 2 要素目に再代入しました。

元 の daishiSkills は、

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

から

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

に変化しましたが、

分割代入で作成した firstSkillotherSkills は、

const firstSkill = JavaScript;

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

と変化していません。

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

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

レスト構文を使用しない分割代入は、
プロパティのキーと同じ変数名を使用する必要がありました(詳細はこちら の記事参照)。

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

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

6.4.1.オブジェクトのとき

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

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

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

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

エラー内容

Cannot redeclare block-scoped variable 'manjuInfo'

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

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

6.4.2.配列のとき

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

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

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

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

エラー内容

Cannot redeclare block-scoped variable 'daishiSkills'

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

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

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

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

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

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

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

6.5.1.オブジェクトのとき

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

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

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

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

エラー内容

A rest element must be last in a destructuring pattern.

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

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

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

6.5.2.配列のとき

配列の分割代入のレスト構文(配置)
const daishiSkills = ['JavaScript', 'TypeScript', 'PHP', 'Ruby', 'AWS'];

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

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

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

エラー内容

A rest element must be last in a destructuring pattern.

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

つまり、

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

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

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

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

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

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

6.6.1.オブジェクトのとき

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

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

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

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

エラー内容

A rest element must be last in a destructuring pattern.

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

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

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

つまり、

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

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

6.6.2.配列のとき

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

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

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

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

エラー内容

A rest element must be last in a destructuring pattern.

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

つまり、配列も

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

ということです。

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

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

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

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

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

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

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

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

6.7.1.オブジェクトのとき

オブジェクトの分割代入のレスト構文(ネスト)
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 オブジェクトを作成した後に、
manjuInfopublickInfo.nickname プロパティと priveteInfo.email プロパティに再代入しました。

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

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

となってほしいところ、

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

となっています。

publicInf.nicknameprivateInfo.email プロパティ共に再代入 の値が入っています。

このことから、

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

ことがわかります。

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

オブジェクトの分割代入のレスト構文(ネストもコピー)
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' }

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

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

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

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

使えとるやん!!

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

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

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

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

6.7.2.配列のとき

配列の分割代入のレスト構文(ネスト)
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 の配列を作成した後に、
daishiSkillsJavaScript'Rubyemail プロパティに再代入しました。

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

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

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

配列の分割代入のレスト構文(ネストもコピー)
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.まとめ

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

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

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

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

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

8.参考

14
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
6