4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【TypeScript】「関数定義の ...arg」と「関数呼び出しの ...arg」は違うんや?(可変長引数、スプレッド構文、オプショナル引数、コールバック関数)

Last updated at Posted at 2022-09-19

1.はじめに

前回の記事は、関数の種類について記事を書きました。

今回は、関数の引数についてまとめました。

実務では、Laravel を使用していますが、今回の記事のレスト構文やスプレッド構文は全く使っていません…

なので、関数の引数にレスト構文やスプレッド構文を使うのは少し混乱しましたが、なんとか理解することができましたのでアウトプットとして記事を投稿しました。

基礎的な内容となっています。

@uhyo さんの「プロを目指す人のための TypeScript 入門」を元にキャッチアップしました。

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

2.目次

1.はじめに
2.目次
3.この記事でわかること
4.環境
5.可変長引数の宣言
 5.1.可変長引数の使い方
 5.2.レスト引数と通常の引数と併用
6.関数呼び出し時に使うスプレッド構文
 6.1スプレッド構文の使い方
7.オプショナル引数の設定
 7.1デフォルト値を設定しないオプショナル引数
 7.2デフォルト値を設定するオプショナル引数
 7.3オプショナル引数の配置
8.コールバック関数
 8.1コールバック関数の使い方
9.おわりに
10.参考

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

本記事は、関数の便利な引数の使い方(基礎)の記事となります。

  • 可変長引数の宣言方法
  • 関数呼び出し時に使うスプレッド構文
  • オプショナル引数の設定
  • 関数の引数に関数を用いるコールバック関数

について理解ができます。

このようなコードを理解することができます。

// 可変長引数を用いた関数を宣言
const giveLikeList = (...articles: string[]): string[] => {
    let result: Array<string> = [];
    for (const article of articles) {
        result.push(`${article}にいいね!する`);
    }
    return result;
};

const articles: string[] = ['この記事', '先週投稿した記事', '1ヶ月前に投稿した記事'];

// スプレッド構文を用いて関数の呼び出し
console.log(giveLikeList(...articles)); 

// オプショナル引数
const giveLikeList = (article: string, isLike?: boolean): string => {
    if (isLike) {
        return `${article}にいいね!する`;
    } else {
        return `${article}にいいね!しない`;
    }
};

// コールバック関数
const likeArticles = articles.map((article: Article): string => {
    return `${article.author}${article.title} にいいね!する`;
});

※ 全てのサンプルコードは、アロー関数を用いて記述しています。

4.環境

  • TypeScript: 4.7.4
  • Node.js: 16.15.1

5.可変長引数の宣言

では最初に、可変長引数について振れていきます。

可変長引数とは、関数が任意の数の引数を受け取れるようにすること です。

レスト構文 を使うことで可変長引数を実現できます。

レスト構文の記事を以前書いたのでご参考にどうぞです。

レスト引数構文と次の項で出てくるスプレッド構文は似ていますが挙動は異なります。

ざっくり説明すると、

  • レスト構文: 複数の要素を集約して1つにまとめること
  • スプレッド構文: 関数の呼び出しや、配列リテラル、オブジェクトリテラルに対して展開するのに使う

です。

本項では、このレスト引数構文を用いて、引数を一つにまとめます。

5.1.可変長引数の使い方

可変長引数(レスト引数構文)は、...引数: 型 と記述して使います。

(レスト構文を引数に使うので、レスト構文引数と表現しています。同じ意味として捉えて頂ければ構いません。)

では、さっそく使っていきます。

// 可変長引数(レスト引数構文)を使用
const giveLikeList = (...articles: string[]): string[] => {
    let result: Array<string> = [];
    for (const article of articles) {
        result.push(`${article}にいいね!する`);
    }
    return result;
};

// 実行結果:[ 'この記事にいいね!する', '先週投稿したに記事にいいね!する', '1ヶ月前に投稿したに記事にいいね!する' ]
console.log(giveLikeList('この記事', '先週投稿した記事', '1ヶ月前に投稿した記事'));

// 実行結果:[]
console.log(giveLikeList());

まずは、ざっくりと関数の説明をします。
「それぞれの引数 article にいいね!するという文字列を返す」関数となっています。
※ 特に、関数には意味はありません

(...articles: string[]) この部分がレスト引数構文です。

レスト構文は、複数の要素を集約して1つにまとめるでしたよね。

このレスト引数に何がはいるかと言うと、
giveLikeList 関数を使用する際に使う引数(’この記事', '先週投稿した記事', '1ヶ月前に投稿した記事')...articlesに代入されます。

giveLikeList('この記事', '先週投稿した記事', '1ヶ月前に投稿した記事')

この部分ですね。

今回は、3つの引数を ...articles に代入していることになります。(3つ未満でも3つを超えても構いません)

(...articles: string[])に戻って細かく見てみます。

引数の型が、文字列の配列型となっています。

レスト引数は任意の数の引数を受け取れるので、配列の文字列型となっています。

もし引数の宣言を、配列にしないと

// 引数を配列にしない
const giveLikeList = (...articles: string): string[] => {
    let result: Array<string> = [];
    for (const article of articles) {
        result.push(`${article}にいいね!する`);
    }
    return result;
};

コンパイルエラーとなります。

エラー内容

A rest parameter must be of an array type.
レスト引数は配列型でなければならない。

レスト引数構文は、要素を1つ1つ記述しなくていいので便利ですね。

5.2.レスト引数と通常の引数と併用

レスト引数は通常の引数と併用して使用できます。

// レスト引数と通常の引数を併用
const giveLikeList = (first: string, ...articles: string[]): string[] => {
    let result: Array<string> = [];
    result.push(`まずは、${first} にいいね!する`);
    for (const article of articles) {
        result.push(`その後、${article}にいいね!する`);
    }
    return result;
};

// 実行結果:[ 'この記事にいいね!する', '先週投稿したに記事にいいね!する', '1ヶ月前に投稿したに記事にいいね!する' ]
console.log(giveLikeList('この記事', '先週投稿した記事', '1ヶ月前に投稿した記事'));

// Expected at least 1 arguments, but got 0.
console.log(giveLikeList());

(first: string, ...articles: string[]) このように、通常の引数とレスト引数を使用できます。

これは、関数呼び出し時の引数の

  • 0 要素目が first に代入
  • 0 要素目以降の残りの要素が ...article に代入

されています。
今回は、通常の引数は一つですが、複数 用いることも可能です。

注意しないといけないポイントが2つあります。

まず、1つ目が関数の呼び出し時の引数です。

5.1.可変長引数の使い方 では関数呼び出し時に の引数でもエラーは出ませんでしが、

// 実行結果:[]
console.log(giveLikeList());

今回の、通所の引数とレスト引数構文を組み合わせたものは、空だとエラーとなります

// Expected at least 1 arguments, but got 0.
console.log(giveLikeList());

レスト引数構文は、任意の数の引数を一つにまとめて配列とします。
なので、レスト引数構文の中身が空でも、空配列となります。

通所の引数は、空では使えないので、

エラー内容

Expected at least 1 arguments, but got 0.
最低一つの引数が必要だが、与えられたのは引数が0こ

というエラーが出てしまいました。

2つ目が、レスト引数構文の記述位置です。

レスト引数構文は引数リストの最後に記述しないといけません。

どういうことかといいますと、

// レスト引数構文の後ろに通常の引数を記述
const giveLikeList = (...articles: string[], end: string): string[] => {
    let result: Array<string> = [];
    for (const article of articles) {
        result.push(`${article}にいいね!する`);
    }
    return result;
};

なぜできないかと言うと、レスト引数は、複数の要素を1つにまとめます。
...article に呼び出しの引数全ての要素が代入されてしまい、end には何も代入することができないからです。

このように、通常の引数とレスト引数構文と組み合わせるときには、
注意点がありますので、使用する際は気をつけないとです。

6.関数呼び出し時に使うスプレッド構文

前項では、可変長引数(レスト引数構文)でしたが、
次は、関数の呼び出し時に使用するスプレッド構文についてです。

以前スプレッド構文の記事も書いているので、ご参考までにどうぞです。

スプレッド構文は、関数の呼び出しや、配列リテラル、オブジェクトリテラルに対して展開するのに使うんでしたね。

これを踏まえて見ていきます。

6.1.スプレッド構文の使い方

スプレッド構文は ...式 という構文で使用します。

では早速使ってみます。

使用する関数は、先程使ったものになります。

const giveLikeList = (...articles: string[]): string[] => {
    let result: Array<string> = [];
    for (const article of articles) {
        result.push(`${article}にいいね!する`);
    }
    return result;
};

const articles: string[] = ['この記事', '先週投稿した記事', '1ヶ月前に投稿した記事'];

// 関数の呼び出しにスプレッド構文を使用
// 実行結果:[ 'この記事にいいね!する', '先週投稿したに記事にいいね!する', '1ヶ月前に投稿したに記事にいいね!する' ]
console.log(giveLikeList(...articles)); 

関数の呼び出しに使用するスプレッド構文は、giveLikeList(...articles) のように記述します。

articles の中に ['この記事', '先週投稿した記事', '1ヶ月前に投稿した記事’] が代入されており、
...articles で展開していることになります。

(スプレッド構文は展開、レスト構文はまとめるでしたね。)

したがって今回であれば、

giveLikeList(...articles);

は、

giveLikeList('この記事', '先週投稿した記事', '1ヶ月前に投稿した記事`);

と同じことになります。

今回は、引数が3つと明確になっているので、あまり必要性が感じれれないかもしれませんが、
もっと多くの要素をもっている場合何が入っているかわからないとき などは非常に便利に使用できます。

スプレッド構文は、レスト引数構文とは異なり、引数リストのどこでもしようすることができます。

/**
 * 実行結果
 * [
 *   'この記事にいいね!する',
 *   '先週投稿した記事にいいね!する',
 *   '1ヶ月前に投稿した記事にいいね!する',
 *   '最初に投稿した記事にいいね!する',
 *   'この記事にいいね!する',
 *   '先週投稿した記事にいいね!する',
 *   '1ヶ月前に投稿した記事にいいね!する'
 * ]
 */
console.log(giveLikeList(...articles, '最初に投稿した記事’, ...articles));

このように使用することもできます。

今回の例のように可変長引数とスプレッド構文を併用して使用します。

理由は、配列の要素が何個あるか不明なことが多いためです。

関数宣言で可変長引数を使用せず に、関数をスプレッド構文で呼び出すときには注意が必要です。

// レスト引数構文を使わないアロー関数
const giveLikeList = (article1: string, article2: string, article3: string): string[] => {
    let result: Array<string> = [];
    for (const article of articles) {
        result.push(`${article}にいいね!する`);
    }
    return result;
};

const articles: string[] = ['この記事', '先週投稿した記事', '1ヶ月前に投稿した記事'];
console.log(giveLikeList(...articles));

このときエラー生じます。

エラー内容

A spread argument must either have a tuple type or be passed to a rest parameter.
スプレッド引数はタプル型であるか、レスト引数に渡される必要があります。

アロー関数の引数が 3 つ、関数呼び出しの引数が 3 つ(スプレッド構文の中身が 3 つ)ですが、
関数の呼び出しの引数が 3 つとは限らないためコンパイルエラーが発生しました。

これを回避するために、

const giveLikeList = (article1: string, article2: string, article3: string): string[] => {
    let result: Array<string> = [];
    for (const article of articles) {
        result.push(`${article}にいいね!する`);
    }
    return result;
};

// 変数の要素数を指定
const articles: [string, string, string] = ['この記事', '先週投稿した記事', '1ヶ月前に投稿した記事'];
console.log(giveLikeList(...articles));

変数 articles の要素が 3 つであることを型で定義することでこのエラーを回避することができます。

複数の引数を簡単に展開できるのでスプレッド構文便利!!!

7.オプショナル引数の設定

次はオプショナル引数の設定です。

まず、オプショナル引数とは何かですが…
あってもいいしなくても良い引数 のことです。

オプショナル引数は、デフォルト値を設定 しないする かで記述が異なります。

7.1.デフォルト値を設定しないオプショナル引数

まずはデフォルト値を設定しない時です。

デフォルト値を設定しないオプショナル引数は、引数名?: 型 と記述し、下記のように使用します。

// デフォルト値を設定しないオプショナル引数
const giveLikeList = (article: string, isLike?: boolean): string => {
    if (isLike) {
        return `${article}にいいね!する`;
    } else {
        return `${article}にいいね!しない`;
    }
};

console.log(giveLikeList('この記事'));        // この記事にいいね!しない
console.log(giveLikeList('この記事', true));  // この記事にいいね!する
console.log(giveLikeList('この記事', false)); // この記事にいいね!しない

ここでは、isLike の判定で、article にいいねするかしないかを決めるような関数に変えています。

isLike?: boolean の部分がデフォルト値を設定しないオプショナル引数です。

これは、関数を呼び出す時に引数を省略すると undefined 型が入ります。

undefined 型は条件分岐では、false に変換されるため、

giveLikeList('この記事')

は、

giveLikeList('この記事’, undefined)

となり、

giveLikeList('この記事’, false)

ということになります。

なので、「この記事にいいね!しない」と返ってきます (← いいねしないはやめてくださいね)

7.2.デフォルト値を設定するオプショナル引数

次にデフォルト値を設定するときです。

変数名: 型 = 式 と記述し、下記のように使用します。

// デフォルト値を設定するオプショナル引数
const giveLikeList = (article: string, isLike: boolean = false): string => {
    if (isLike) {
        return `${article}にいいね!する`;
    } else {
        return `${article}にいいね!しない`;
    }
};

console.log(giveLikeList('この記事'));        // この記事にいいね!しない
console.log(giveLikeList('この記事', true));  // この記事にいいね!する
console.log(giveLikeList('この記事', false)); // この記事にいいね!しない

関数は先程と同じ内容です。

isLike: boolean = false の部分がデフォルト値を設定するオプショナル引数です。

これは、引数を省略するとデフォルト値の false を使用することになります。

giveLikeList('この記事')

は、

giveLikeList('この記事’, false)

となります。

7.3.オプショナル引数の配置

オプショナルな引数よりもあとに、普通の引数を置くことはできません。

どういうことかといいますと、

// デフォルト値を設定しないオプショナル引数
const giveLikeList = (isLike?: boolean, article: string): string => {
    if (isLike) {
        return `${article}にいいね!する`;
    } else {
        return `${article}にいいね!しない`;
    }
};

エラー内容

A required parameter cannot follow an optional parameter.
必須引数は、オプショナル引数の後に続くことはできません。

このように、デフォルト値を設定しないオプショナル引数は、コンパイルエラーで弾かれてしまいます。

では、次にデフォルト値を設定するオプショナル引数で見てみると、

// デフォルト値を設定するオプショナル引数
const giveLikeList = (isLike: boolean = false, article: string): string => {
    if (isLike) {
        return `${article}にいいね!する`;
    } else {
        return `${article}にいいね!しない`;
    }
};

console.log(giveLikeList( 'この記事'));       // Expected 2 arguments, but got 1.
console.log(giveLikeList(true, 'この記事'));  // この記事にいいね!する
console.log(giveLikeList(false, 'この記事')); // この記事にいいね!しない

エラー内容

Expected 2 arguments, but got 1.
2つの引数を期待したが、1つになった。

とアロー関数部分ではコンパイルエラーとなりませんが、関数を呼び出す時にエラーとなります。

以下は、私の解釈です。間違っていたらご指摘ください。、
エラー内容は異なりますが、オプショナル引数を省略したかどうかが判断できないためエラーが生じました。
(私の解釈です。間違っていたらご指摘ください。)

したがって、オプショナル引数とレスト引数とでは、併用することができません。

オプショナル引数の後に普通の引数は書けない。
レスト引数の後ろに引数は書けない。

これらの条件より、併用することができません。

8.コールバック関数

関数の引数について記述してきました。
最後に、関数の引数に関数を渡すコールバック関数に振れておきます。

8.1.コールバック関数の使い方

map 関数を例にして見ていきます。

type Article = {
    author: string;
    title: string;
};

const giveLike = (article: Article): string => {
    return `${article.author}${article.title} にいいね!する`;
};

const articles: Article[] = [
    {author: 'daishiman', title: 'この記事'},
    {author: 'manju', title: 'あの記事'}
];

// 関数の引数に関数を使用(コールバック関数)
const likeArticles: string[] = articles.map(giveLike);
console.log(likeArticles);  // [ 'daishiman の この記事 にいいね!する', 'manju の あの記事 にいいね!する' ]

map 関数の引数に giveLike 関数を入れています。

const likeArticles: string[] = articles.map(giveLike);

map 関数の引数として使う giveLIke 関数自体は、
引数 article を渡すことで ${article.author} の ${article.title} にいいね!する という文字列を返します。

map 関数は、

呼び出し元の配列内のすべての要素に対して提供された関数を呼び出した結果が入力され た新しい配列を作成します。

MDN の Array.prototype.map() 参照

したがって、

articles.map(giveLike);

は、articles の各要素を giveLike 関数に繰り返し適用して新しい配列を返します。

なので、

[
  'daishiman の この記事 にいいね!する',
  'manju の あの記事 にいいね!する'
]

を返します。

giveLike 関数の返り値の型は、string 型で、それを繰り返し適用して配列に返すので articles.map(giveLike);stirng[] 型となります。

ちなみに、このgiveLike 関数を直接 map 関数の引数に渡すこともできます。

const likeArticles = articles.map((article: Article): string => {
    return `${article.author}${article.title} にいいね!する`;
});

今回は map 関数を用いましたが、便利な関数はまだまだあるので、調べながら使っていきたいと思います。

9.おわりに

関数の引数としてレスト構文、スプレッド構文を使用することで、楽に便利に関数を使用することができます。

いくつか注意点等はありますが、TypeScript を使っていく上で必要不可欠なのでどんどん使いながら慣れていきたいと思います。

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

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

10.参考

書籍:プロを目指す人のためのTypeScript入門 鈴木僚太[著] @uhyo
【JavaScript】「スプレッド構文」と「レスト構文」について
MDN の Array.prototype.map()

4
2
1

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
4
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?