【TypeScript】「メソッド記法の関数」よりも「通常の関数型」を使うべき理由を簡単に解説

# 1.はじめに
過去に、関数型の部分型関係の特性の記事を書いてきました。

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

https://qiita.com/daishiman/items/91d4ea25fe391217a00b

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

本記事では、メソッド記法が部分型の特性を緩和しているため、できるだけ **使用しないほうがいい** 理由についてまとめました。

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

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



# 2.目次
[1.はじめに](#1はじめに)  
[2.目次](#2目次)  
[3.この記事でわかること](#3この記事でわかること)  
[4.環境](#4環境)  
[5.メソッド記法の関数は部分型関係を緩和する理由](#5メソッド記法の関数は部分型関係を緩和する理由)  
　[5.1.通常の関数型とメソッド記法の違い](#51通常の関数型とメソッド記法の違い)  
　[5.2.メソッド記法での関数宣言が部分型関係を緩和する理由](#52メソッド記法での関数宣言が部分型関係を緩和する理由)  
[6.おわりに](#6おわりに)  
[7.参考](#7参考)  





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

メソッド記法で宣言した関数は、

:::note alert 

引数に SubType を受け取る関数を引数に SuperType を受け取る関数の代わりに扱うことはできない。

:::

という部分型関係の特性を緩和してしまいます。

下記のコードを用いて説明していきます。

```ts
type Engineer = {
    name: string;
    year: number;
};

type FrontendEngineer = {
    name: string;
    year: number;
    frontendSkill: string;
};

type SelfIntroduction = {
    func: (engineer: Engineer) => string;
    method(engineer: Engineer): string;
}

const selfIntroductionEngineer: SelfIntroduction = {
    func: engineer => `名前は${engineer.name}です。実務経験は${engineer.year}です。`,
    method: engineer => `名前は${engineer.name}です。実務経験は${engineer.year}です。`,
};

const doSelfIntroduction = (frontendEngineer: FrontendEngineer) => `
    名前は${frontendEngineer.name}です。
    実務経験は${frontendEngineer.year}です。
    ${frontendEngineer.frontendSkill}ができます。
`;

// エラー: Type '(frontendEngineer: FrontendEngineer) => string' is not assignable to type '(engineer: Engineer) => string'.
selfIntroductionEngineer.func = doSelfIntroduction;

// これはエラーが発生しない
selfIntroductionEngineer.method =  doSelfIntroduction;
```

# 4.環境

- TypeScript: 4.7.4
- Node.js: 16.15.1

# 5.メソッド記法の関数は部分型関係を緩和する

ここから、「なぜメソッド記法での関数宣言が部分型関係を緩和するのか？」を見ていきますが、
まず、通常の関数型とメソッド記法の関数宣言について簡単にさらっておきたいと思います。

## 5.1.通常の関数型とメソッド記法の違い

メソッド記法は、オブジェクトリテラル （`{ プロパティ: 式 }` のこと）の中で関数を作成するための記法です。

以前の記事にしていますので、詳細はこちらをごらん頂ければです。

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

今回は、 `SelfIntroduction` というオブジェクト型で通常の関数とメソッド記法での関数の型定義をしています。

```ts
type SelfIntroduction = {
    func: (engineer: Engineer) => string;
    method(engineer: Engineer): string;
}
```

- `func` は、通常の関数型で宣言
- `method` は、メソッド記法で宣言

オブジェクト型の中で関数型を表現する場合、

- 通常の関数型： `関数名: (引数リスト) => 返り値の型`
- メソッド記法： `メソッド名(引数リスト): 返り値の型`

となります。


通常の関数とメソッド記法の違いがわかったところで、  
本題の「メソッド記法での関数宣言が部分型関係を緩和するのか」を見ていきます。




## 5.2.メソッド記法での関数宣言が部分型関係を緩和する理由

では、本題の「メソッド記法での関数宣言が部分型関係を緩和する理由」について見ていきます。 

冒頭にも書きましたが、次のソースコードを用いて見ていきたいと思います。


```ts
// SuperType のオブジェクトの定義 ...①
type Engineer = {
    name: string;
    year: number;
};

// SubType のオブジェクトの定義 ...②
type FrontendEngineer = {
    name: string;
    year: number;
    frontendSkill: string;
};

// 引数で SupetType の通常の関数とメソッド記法の関数をもったオブジェクトの型定義 ... ③
type SelfIntroduction = {
    func: (engineer: Engineer) => string;
    method(engineer: Engineer): string;
}

// ③ の型のオブジェクトを作成 ...④
const selfIntroductionEngineer: SelfIntroduction = {
    func: engineer => `名前は${engineer.name}です。実務経験は${engineer.year}です。`,
    method: engineer => `名前は${engineer.name}です。実務経験は${engineer.year}です。`,
};

// 引数で②の SubType を受け取る関数 ...㋐
const doSelfIntroduction = (frontendEngineer: FrontendEngineer) => `
    名前は${frontendEngineer.name}です。
    実務経験は${frontendEngineer.year}です。
    ${frontendEngineer.frontendSkill}ができます。
`;

// ④のオブジェクトの通常の関数型で宣言した関数に㋐の関数を代入 ... ㋑
// エラー: Type '(frontendEngineer: FrontendEngineer) => string' is not assignable to type '(engineer: Engineer) => string'.
selfIntroductionEngineer.func = doSelfIntroduction;

// ④のオブジェクトのメソッド記法で宣言した関数に㋐の関数を代入 ...㋒
// これはエラーが発生しない
selfIntroductionEngineer.method =  doSelfIntroduction;
```

ここでは、3 つの関数が存在します。

- 関数 `doSelfIntroduction` （㋐）  
`(frontendEngineer: FrontendEngineer) => string` 型

- 関数 `selfIntroductionEngineer.func`（通常の関数型）（㋑）  
`(engineer: Engineer) => string` 型

- 関数 `selfIntroductionEngineer.method`（メソッド記法の関数）（㋒）  
`(engineer: Engineer) => string` 型
 
整理すると、  

- ㋐は、引数で `FrontendEngineer` 型（②のSubType）を受け取り、`string` 型を返す関数
- ㋑㋒のどちらも、引数で `Engineer` 型（①のSuperType）を受け取り `string` 型を返す関数

となっています。

そして、  
関数 `selfIntroductionEngineer.func`（㋑）と関数 `selfIntroductionEngineer.method`（㋒）に関数 `doSelfIntroduction` （㋐）を代入しています。

```ts
// ④ オブジェクトの通常の関数型で宣言した関数に㋐の関数を代入 ... ㋑
selfIntroductionEngineer.func = doSelfIntroduction;

// ④ オブジェクトのメソッド記法で宣言した関数に㋐の関数を代入 ...㋒
selfIntroductionEngineer.method =  doSelfIntroduction;
```

しかし、これらは、 **引数の型による部分型関係** を違反しています。

詳細は、[【TypeScript】関数型の引数の型による部分型関係ややこしすぎません？なので、初心者にも理解できるように噛み砕いてみた](https://qiita.com/daishiman/items/e28a810323ae8b1b976f) の記事に書いていますが、  
ざっくりいうと、

:::note info

OK

引数に **SuperType** を受け取る関数を、引数に **SubType** を受け取る関数の代わりに扱うことができる。

:::

:::note alert

NG

引数に **SubType** を受け取る関数を、引数に **SuperType** を受け取る関数の代わりに扱うことはできない。

:::

というのが部分型関係の特性となっています。

今回は、NG である 「引数で **SubType** を受け取る関数（㋐）を、引数で **SuperType** を受け取る関数（㋑㋒）の代わりに扱おう」としています。

なので、コンパイルエラーが生じるはずです。

では、**SubType** の引数を受け取る関数 `doSelfIntroduction` （㋐）を、代入した各関数を見てみましょう。

通常の関数型で宣言されたの関数 `selfIntroductionEngineer.func` （㋑）はエラーとなっていますね。

```ts
// ④ オブジェクトの通常の関数型で宣言した関数に㋐の関数を代入 ... ㋑
// エラー: Type '(frontendEngineer: FrontendEngineer) => string' is not assignable to type '(engineer: Engineer) => string'.
selfIntroductionEngineer.func = doSelfIntroduction;
```

しかし、メソッド記法で宣言されたの関数 `selfIntroductionEngineer.method` （㋒）は **エラーとなっていません** 。

```ts
// ④ オブジェクトのメソッド記法で宣言した関数に㋐の関数を代入 ...㋒
// これはエラーが発生しない
selfIntroductionEngineer.method =  doSelfIntroduction;
```

これは、  
「引数で **SubType** を受け取る関数を引数で **SuperType** を受け取る関数の代わりに扱うことはできない」   
という部分型関係の特性を緩和しています。

**型の安全性を破壊しちゃっていますね…**

つまり、  
「**メソッド記法で宣言された関数** は部分型関係の性質を **緩和** している」  
ということになります。




# 6.おわりに

メソッド記法は、部分型関係の特性を緩和している理由がわかったかと思います。

これからは、オブジェクトリテラル内で関数を宣言するときは、  
メソッド記法を使わずに通常の関数型で宣言するように心がけたいと思います。

やっと、部分型関係についてわかってきたけど、やっぱり難しいですね…

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

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

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

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

https://qiita.com/daishiman/items/1517f397e1903339ce9c


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


# 7.参考

[書籍：プロを目指す人のためのTypeScript入門　鈴木僚太［著］](https://twitter.com/uhyo_/status/1517158176365641729?s=20&t=5jZBSZVbLrAsgkCUruO7Ig)