Destructuring を用いた関数の引数を初期化する方法


Destructuring とは

Destructuring assignment は ES6 で導入された JavaScript の記法です。
主に、配列やオブジェクトで用いられ、非常にパワフルに活躍します。

今回は、関数の引数として用いるケースについて紹介します。

利用例

下記のような二種類の関数を考えます。

const normalFunc = (arg1?, arg2?, arg3?) => {
  ...
}

const destructFunc = ({arg1, arg2, arg3}) => {
  ...
}

それぞれの関数に対して、arg3だけに値を設定して使いたいケースでは、それぞれ、

normalFunc(null, null, 'something');

destructFunc({ arg3: 'something' });

と書きます。
Destructuring assignment を用いた場合、他の変数を考慮しなくて済みます。

引数を初期化する

では、引数を初期化したい場合はどうなるでしょうか。
通常の関数の場合は、以下のように書くことができます。

const normalFunc = (arg1 = 1, arg2 = 2, arg3 =3) => {
  return { arg1, arg2, arg3 };
}

console.log(normalFunc());
// Output: {arg1: 1, arg2: 2, arg3: 3}
console.log(normalFunc(10));
// Output: {arg1: 10, arg2: 2, arg3: 3}
console.log(normalFunc(undefined, undefined, 10));
// Output: {arg1: 1, arg2: 2, arg3: 10}

Destructuring を用いた場合、どのように記載できるでしょうか?

const destructFunc = ({arg1 = 1, arg2 = 2, arg3 = 3}) => {
  return { arg1, arg2, arg3 };
}

console.log(destructFunc({arg1:10}));
// Output: {arg1: 10, arg2: 2, arg3: 3}
console.log(destructFunc({arg3:10}));
// Output: {arg1: 1, arg2: 2, arg3: 10}

arg3を設定するときにundefinedを追加しなくても動作するので良い感じですね。

ただ、実はこれだと少し不足があります。引数を取り除いてみます。

console.log(destructFunc());
// Output: TypeError

通常の関数では、すべての引数をオプショナルで渡していたので、normalFunc()と記載することができました。
一方で、 Destructuring を用いた関数では、オブジェクトを引数として渡さないといけないので、destructFunc({})と記載する必要があります。
これだと毎回 {} を書かないといけないので少し不便です。

destructFunc()に対応する

引数なしの場合のケースは以下のように書くことができます。

const destructFunc = (
  {arg1 = 1, arg2 = 2, arg3 = 3} = {
    arg1: 1,
    arg2: 2,
    arg3: 3,
  },
) => {
  return { arg1, arg2, arg3 };
}

初期化の部分で少し可読性が低いですね。

以下の形で書くこともできます。
この場合、return { arg1, arg2, arg3 }と省略できなくなります。

const destructFunc = ({arg1, arg2, arg3} = {}) => {
  return {
    arg1: arg1 || 1,
    arg2: arg2 || 2,
    arg3: arg3 || 3,
  };
}

TypeScript を用いるとこれらを改善できます。

interface Params {
  arg1?: number;
  arg2?: number;
  arg3?: number;
}
const destructFunc = ({...props}: Params = {}) => {
  const { arg1 = 1, arg2 = 2, arg3 = 3 } = props;
  return {
    arg1,
    arg2,
    arg3,
  };
}

引数はParamsによって定義されているので明確です。
初期化処理も、繰り返しを避けることができました。

記事の内容に不備、誤りなどがある場合はご指摘いただけると幸いです。
また、よりスマートな記法をご存知であれば是非教えていただきたいです。

読んでいただきありがとうございました。