1.はじめに
今回の記事は、さまざまな関数についてまとめました。
私は、バックエンドエンジニアとして実務を行っており、
関数宣言しか使って来ませんでした(あとコールバック関数も多少使ってます)。
アロー関数やメソッド記法に関しては、見たことある程度の理解度でした。
本記事を書くことで関数についての理解が深まりました。
同じ境遇の方に向けて書いている記事となります。
なので基礎的な内容となっています。
@uhyo さんの「プロを目指す人のための TypeScript 入門」を元にキャッチアップしました。
間違って解釈している所ありましたら、ご指摘いただけますと幸いです。
2.目次
1.はじめに
2.目次
3.この記事でわかること
4.環境
5.関数とは
5.1.関数宣言で関数を作る
5.1.1.返り値がある関数
5.1.2.返り値がない関数
5.2.関数式で関数を作る
5.2.1.関数式の関数
5.2.2.分割代入の関数式
5.3.アロー関数で関数を作る
5.3.1.アロー関数
5.3.2.アロー関数の省略系
5.4.メソッド記法で関数を作る
5.4.1.メソッド記法
5.4.2.メソッド記法と通常の記法+アロー関数を比較する
6.おわりに
7.参考
3.この記事でわかること
下記の関数の違いがわかるようになります。
// 関数宣言
function giveLike(name: string, title: string): string {
return `${name} の ${title} にいいね!する`;
}
// 関数式
const giveLike = function (name: string, title: string): string {
return (`${name} の ${title} にいいね!する`);
};
// アロー関数
const giveLike = ({name, title}: ArticleInfo): string => {
return (`${name} の ${title} にいいね!する`);
};
// メソッド記法
const articleGoodOrBad = {
goodArticle: (name: string, title: string): string => `${name} の ${title} にいいね!する`,
};
4.環境
- TypeScript: 4.7.4
- Node.js: 16.15.1
5.関数とは
関数は、値の一種でありオブジェクトです。
関数を使うことで同じ処理を繰り返さずに再利用できます。
本記事では、関数宣言、関数式、アロー関数式、メソッド記法の関数について比較して見ていきたいと思います。
補足事項等はあるかと思いますが、関数を比較するのが目的となっています。
ご理解の程お願いいたしますm(_ _)m
5.1.関数宣言で関数を作る
関数宣言は、最もベーシックな関数の定義になります。
実務でバックエンドをメインにやっている私には一番馴染みがある関数になります。
関数宣言は、下記のような構成で記述します。
関数宣言の構成
function 関数名(引数リスト): 返り値の型 { 中身 }
では、一つ一つの構成要素を説明します。
- 関数名は、関数を入れる箱の名前で識別子(他と区別するための名前)
- 引数リストは、関数に渡す変数で
変数名: 型
という風に使う。リストなので、複数設定できる - 返り値の型は、関数で返ってくる値の型を指定する
- 中身は、関数が行う処理
ざっくりした説明ですが、このような構成で関数宣言はできています。
基本的に他の関数も、同じような構成要素を使っています。
5.1.1.返り値がある関数
まずは、返り値がある関数から見ていきます。
// 関数宣言で関数を作る
function giveLike(name: string, title: string): string {
return `${name} の ${title} にいいね!する`;
}
// daishiman の この記事 にいいね!する
console.log(giveLike('daishiman', 'この記事'));
関数を詳細に見てみます。
-
giveLike
という関数を宣言 - 引数は、
name
、title
を使う -
name
は、string
型 -
title
は、string
型 - 返り値の型は、
stiring
型 - 関数の中身は、
${name} の ${title} にいいね!する
という関数になっています。
そして、この関数を
console.log(giveLike('daishiman', 'この記事'));
で使用しています。
giveLike
という関数に、'daishiman'
、'この記事'
という値を入れて、
「daishiman の この記事 にいいね!する」
という値が返ってきています。
このように、中身の処理を気にすることなく、値を返すことができるのが関数となります。
そして、引数を変ることで、別の値を返すことができるので使い回すことができます。
引数の方や返ってくる値の方が宣言とことなるとエラーとなります(当たり前ですが…)。
引数に宣言した型以外を代入してみます。
console.log(giveLike(1, 'この記事'));
name
には string
型が入らないといけないですが、number
型を入れると、
エラー内容
Argument of type 'number' is not assignable to parameter of type 'string'.
数値型の引数は、文字列型のパラメータに割り当てることができません。
このようにエラーが出ます。
まあ当たり前ですが、設定した型にしましょねといわれていますね。
返り値の型宣言と異なる型を返してみます。
function giveLike(name: string, title: string): string {
return true;
}
エラー内容
Type 'boolean' is not assignable to type 'string'.
'boolean' 型は 'string' 型に代入できません。
あと、関数宣言の特徴として、関数宣言よりも前に関数を使うことができます。
// 関数宣言よりも前に関数を使う
// daishiman の この記事 にいいね!する
console.log(giveLike('daishiman', 'この記事'));
function giveLike(name: string, title: string): string {
return `${name} の ${title} にいいね!する`;
}
この挙動を 巻き上げ といいます。
まあ、色々と書きましたが、何がいいたいかと言うと、関数は使い回すことができるので便利だよ ってことです。
5.1.2.返り値がない関数
先程は、関数の値を stirng
型で返していましたが、次は値を返さない関数を見てみます。
関数の構成は先程の関数の構成と同じです。
関数宣言(返り値がない関数)の構成
function 関数名(引数リスト): 返り値の型 { 中身 }
返り値の方が void
型となり、中身に return
を使わないで値を返さないのが少し異なります。
では見てみましょう。
function giveLike(name: string, title: string): void {
console.log(`${name} の ${title} にいいね!する`);
}
// daishiman の この記事 にいいね!する
console.log(giveLike('daishiman', 'この記事'));
関数の返り値の型が void
型、関数の中身に return
がない事以外は先程のコードと同じです。
(使用方法も同じ)
「あれ?関数を実行すると値を返しとるやんけ」
と思うかもしれませんが、これは、値を返しているわけではありません。
console.log
で関数の中身を覗いているだけです。
return
を使わないといいましたが、返り値がない関数でも return
を使う事もあります。
どんなときかと言うと、
早期リターンを使うときです。
早期リターンとは、条件分岐で処理を中断させることです。
// 早期リターンで関数宣言
function giveLike(name: string, title: string, isLike: boolean): void {
if (isLike === false) {
return;
}
if (isLike === true) {
console.log(`${name} の ${title} にいいね!する`);
}
}
// undefined
console.log(giveLike('daishiman', 'この記事', false));
(記述的にはちょっとまどろっこしいかもしれませんが、早期リターンに注目したいということで勘弁してください)
このように、
isLike
が false
のときは、値を何も返さないで処理を終わらせる
というようにすることを早期リターンといいいます。
このときは、void
型でも return
を使います。
5.2.関数式で関数を作る
次は、関数式の関数を見てみます。
関数宣言は、中身の部分が文で関数を宣言していましたが、
関数式は、言葉の通り 式 で関数を宣言します。
関数式は下記の構成で記述します。
関数式の構成
function (引数リスト): 返り値の型 { 中身 };
関数宣言とほとんど変わりませんが、function
の直後に関数名を記述していません。
先程もいいましたが、この関数は、式です。
(結果があるものを式といいます。)
式の結果が関数式で作られた関数になります。
そして、この関数を使うには変数に入れます。
(変数に入れずにすぐに呼び出すこともできます)
ちょっと何言っているかわからないと思いますので、コードを見てみましょう。
5.2.1.関数式の関数
// 関数式で関数を作成
const giveLike = function (name: string, title: string): string {
return (`${name} の ${title} にいいね!する`);
};
// daishiman の この記事 にいいね!する
console.log(giveLike('daishi', 'この記事'));
※ 関数の処理は、関数宣言のときと同じです。
下記の部分が関数式(式)となります。
function (name: string, title: string): string {
return (`${name} の ${title} にいいね!する`);
};
変数 giveLike
の中に関数式が入っています。
変数 giveLike
の中身は、関数式で作られた関数ということになります。
なので、giveLike
を関数として使うことができます。
関数宣言のときは、巻き上げをできましたが、関数式の場合は巻き上げはできません。
// daishiman の この記事 にいいね!する
console.log(giveLike('daishi', 'この記事'));
const giveLike = function (name: string, title: string): string {
return (`${name} の ${title} にいいね!する`);
};
エラー内容
Block-scoped variable 'giveLike' used before its declaration.
ブロック・スコープ付き変数 'giveLike' が宣言前に使用されました。
これは、変数宣言をする前に変数を呼び出そうとしたためコンパイルエラーとなりました。
5.2.2.分割代入の関数式
関数の引数に分割代入を使用することができます。
分割代入に関しては、以前記事を書きましたのでご参考にどうぞです。
(ご覧いただいた際についでに いいね してくださると嬉しいです😆 w)
では、分割代入の関数式を作成する前に先程の関数式のname: string, title: string
の部分を
下記のようにArticleInfo
オブジェクトを受け取る形に改造します。
type ArticleInfo = {
name: string;
title: string;
};
// 引数リストをオブジェクトに変更
const giveLike = function (articleInfo: ArticleInfo): string {
return (`${articleInfo.name} の ${articleInfo.title} にいいね!する`);
};
const daishiArticle: ArticleInfo = {
name: 'daishiman',
title: 'この記事',
};
// daishiman の この記事 にいいね!する
console.log(giveLike(daishiArticle));
type ArticleInfo = {
name: string;
title: string;
};
この部分です、ArticleInfo
型は string
型の name
プロパティ、string
型の titiel
プロパティをもつオブジェクトとすることを宣言しています。
次に、
function (name: string, title: string): string {
return (`${name} の ${title} にいいね!する`);
};
の関数式の引数リストを ArticleInfo
型のオブジェクトを使うように変更します。
const giveLike = function (articleInfo: ArticleInfo): string {
return (`${articleInfo.name} の ${articleInfo.title} にいいね!する`);
};
では、分割代入に変換してみます。
type ArticleInfo = {
name: string;
title: string;
}
// 分割代入を使って関数式で関数を作成
const giveLike = function ({name, title}: ArticleInfo): string {
return (`${name} の ${title} にいいね!する`);
};
const daishiArticle = {
name: 'daishiman',
title: 'この記事',
};
// daishiman の この記事 にいいね!する
console.log(giveLike(daishiArticle));
どこが変わったか見ていきます。
関数式の引数リストが
articleInfo: ArticleInfo
→ {name, title}: ArticleInfo
に変更しています。
つまりこれは、
{name, title}: ArticleInfo = articleInfo;
となり、これを分解して
const name = articcleInfo.name;
const title = articcleInfo.title
と同じ意味になり、
function ({name, title}: ArticleInfo): string {
return (`${name} の ${title} にいいね!する`);
};
に変換できます。
分割代入で記述するとスッキリしますね。
関数宣言でも同じように分割代入を使うことができます。
5.3.アロー関数で関数を作る
アロー関数は、関数式の一種で =>
という矢印を使う関数です。
アロー関数は、下記のような構成で記述します。
アロー関数の構成
(引数リスト): 返り値の型 => { 中身 };
5.3.1.アロー関数
関数式で使った式をアロー関数に変換すると下記になります。
type ArticleInfo = {
name: string;
title: string;
}
// アロー関数で関数を作成
const giveLike = ({name, title}: ArticleInfo): string => {
return (`${name} の ${title} にいいね!する`);
};
const daishiArticle: ArticleInfo = {
name: 'daishiman',
title: 'この記事',
};
// daishiman の この記事 にいいね!する
console.log(giveLike(daishiArticle));
では、関数式とアロー関数での記述を比較してみます。
関数式で記述
const giveLike = function ({name, title}: ArticleInfo): string {
return (`${name} の ${title} にいいね!する`);
};
アロー関数で記述
const giveLike = ({name, title}: ArticleInfo): string => {
return (`${name} の ${title} にいいね!する`);
};
では、変わったところを見てみましょう。
-
function
を省略 -
=>
を使用
これだけですが、省略してスッキリしましたね。
厳密には this
の扱いなど違いがあるようですが、処理結果は同じ値となりました。
今後この this
の使い方については、記事にしたいと思います。
5.3.2.アロー関数の省略系
関数式を省略したのがアロー関数ですが、さらにアロー関数を省略して記述ができます。
宣言は、下記のような構成で記述します。
アロー関数の省略系の構成
(引数リスト): 返り値の型 => 式
先程のアロー関数を変換すると下記のようになります。
type ArticleInfo = {
name: string;
title: string;
}
// アロー関数の省略系で関数を作成
const giveLike = ({name, title}: ArticleInfo): string => (`${name} の ${title} にいいね!する`);
const daishiArticle: ArticleInfo = {
name: 'daishiman',
title: 'この記事',
};
// daishiman の この記事 にいいね!する
console.log(giveLike(daishiArticle))
では、アロー関数と省略系での記述を比較してみます。
アロー関数で記述
const giveLike = ({name, title}: ArticleInfo): string => {
return (`${name} の ${title} にいいね!する`);
};
省略系で記述
const giveLike = ({name, title}: ArticleInfo): string => `${name} の ${title} にいいね!する`;
このように1行で記述することができます。
では、変わったところを見てみましょう。
-
{ }
を省略 -
( )
を省略 -
return
を省略
と、様々な点を省略することができます。
ただ、注意点があり、
返り値としてオブジェクトリテラル({ プロパティ: 式 }
となっているもの)の式としたときは ( )
で囲む必要があります。
どういうことかといいますと、
type ArticleInfo = {
name: string;
title: string;
};
type ArticleObj = {
name: string;
};
// アロー関数の省略系で返り値がオブジェクトリテラルのとき
const giveLike = ({name, title}: ArticleInfo): ArticleObj => ({ name: title });
const daishiArticle: ArticleInfo = {
name: 'daishiman',
title: 'この記事',
};
アロー関数の省略系の返り値がオブジェクトリテラルとなるときは、
このように ({ name: title });
オブジェクトリテラルを ( )
で囲う必要があるとということです。
もし ( )
で囲わずにコンパイルすると
const giveLike = ({name, title}: ArticleInfo): ArticleObj => { name: title };
このようなエラーが出ます。
エラー内容
A function whose declared type is neither 'void' nor 'any' must return a value.
宣言された型が 'void' でも 'any' でもない関数は、値を返さなければなりません。
オブジェクトリテラルを返すアロー関数の省略系に ( )
がないと
通常のアロー関数の中身を囲う { }
と勘違いしてしまいこのようなエラーとなります。
なので、本来 ArtcicleObj
型で返ってこないといけないところ、別の型(void
、any
型の値を返さない型)で返ってきているよって注意されちゃいました
。
5.4.メソッド記法で関数を作る
最後にメソッド記法の関数です。
これは、オブジェクトリテラルの中で使用する関数です。
メソッド記法の関数はこのように記述します。
メソッド記法の構成
プロパティ名(引数リスト): 返り値の型 { 中身 }
5.4.1.メソッド記法
それでは、コードを見てみましょう。
const articleGoodOrBad = {
goodArticle(name: string, title: string): string { return `${name} の ${title} は Good 記事` },
badArticle(name: string, title: string): string { return `${name} の ${title} は Bad 記事` },
};
console.log(articleGoodOrBad.goodArticle('daishi','この記事')); // daishi の この記事 は Good 記事
console.log(articleGoodOrBad.badArticle('daishi','あの記事')); // daishi の あの記事 は Bad 記事
オブジェクトリテラル({ プロパティ: 式 }
となっているもの)で囲われたオブジェクトが変数 articleGoodOrBad
の中に代入されています。
goodArticle(name: string, title: string): string { return `${name} の ${title} は Good 記事` },
この部分を分解してみると
-
goodArticle(name: string, title: string)
がプロパティ -
string { return ${name} の ${title} は Good 記事 }
が値
となります。
badArticle
も同じ処理をしています。
この関数を参照したいときは、
関数の参照方法
オブジェクト.関数(引数リスト)
というふうに使います。
5.4.2.メソッド記法と通常の記法+アロー関数を比較する
先程のメソッド記法のオブジェクトを通常の記法+アロー関数 にして変換して比較してみます。
メソッド記法
const articleGoodOrBad = {
goodArticle(name: string, title: string): string { return `${name} の ${title} は Good 記事` },
badArticle(name: string, title: string): string { return `${name} の ${title} は Bad 記事` },
};
console.log(articleGoodOrBad.goodArticle('daishi','この記事')); // daishi の この記事 は Good 記事
console.log(articleGoodOrBad.badArticle('daishi','あの記事')); // daishi の あの記事 は Bad 記事
通常の記法+アロー関数
const articleGoodOrBad = {
goodArticle: (name: string, title: string): string => `${name} の ${title} は Good 記事`,
badArticle: (name: string, title: string): string => `${name} の ${title} は Bad 記事`
};
console.log(articleGoodOrBad.goodArticle('daishi','この記事')); // daishi の この記事 は Good 記事
console.log(articleGoodOrBad.badArticle('daishi','あの記事')); // daishi の あの記事 は Bad 記事
似たような記述ですが、少し違います。
通常の記法+アロー関数の方では、
goodArticle: (name: string, title: string): string => `${name} の ${title} は Good 記事`,
-
goodArticle
がプロパティ -
(name: string, title: string): string =>
${name} の ${title} は Good 記事
となっています。
関数の参照の仕方は同じです。
ただ、メソッド記法を使うときは使わないときとでは、細かな違いが生じるようです。
詳細は、また記事にしたいと思いますが、
- メソッド記法は、型の安全を壊す
- this の扱いがことなる
といったデメリットが生じます…
6.おわりに
様々な関数の作り方があり、さらに省略系も絡んでくると混乱しがちですが、
基本の関数の記述方法を理解することで混乱が解消されました。
併せて他の記事も読んでいただけると嬉しいです🙇♂️
最後まで読んでいただきありがとうございました。