【TypeScript】関数の型を書く方法忘れるのでまとめ

TypeScriptで関数を書くときに色々な書き方があり、よく忘れてしまうので個人的な備忘録として記事にいたします。

~~関数の書き方が色々ありすぎて面倒臭い~~

# 1. Class内メソッドのケース

```typescript:定型文
functionName(args: argsType): returnType {
  // run process
}

```

```typescript:使用例
export class Personal {
  constructor(private _firstName: string, private _lastName: string) {}

  sayHello(name: string): string {
    return `Hello! ${name}`;
  }
}
```

# 2. 関数の型だけを定義するケース
続いては、関数の型だけを定義するケースです。
多くのパターンがありますので、個別に見てみます。

## 2-1. Class内に型を定義するケース
現在非推奨ですが、Nuxt.js の `nuxt-property-decorator` などを使用するケースなどです。

```typescript:定型文
functionType!: (args: argsType) => returnType;
```

```typescript:使用例
export default class Index extends Vue {
  fetchFormData!: (args: string) => Promise<void>;

  async init() {
    await this.fetchFormData('hoge');
  }

  async created() {
    await this.init();
  }
}
```

## 2-2.抽象クラスとして定義するケース

```typescript:定型文
export abstract class Personal {
  abstract functionType: (args: argsType) => returnType;
}
```

## 2-3. typeで型を定義するケース

```typescript:定型文
type FunctionName = (args: argsType) => returnType;

const functionName: FunctionName = (args) => {
  // run process
}
```

```typescript:使用例
type Calculate = (price: number, tax: number) => number;

const calculate: Calculate = (price, tax) => {
  return price * tax;
};

const totalPrice = calculate(100, 10);
```

## 2-4. interfaceで型を定義するケース

```typescript:定型文
interface FunctionType {
  (args: argsType): returnType;
};

const functionName: FunctionType = (args) => {
  return args;
};
```

```typescript:使用例
interface Calculate {
  (price: number, tax: number): number;
}

const calculate: Calculate = (price, tax) => {
  return price * tax;
};

const totalPrice = calculate(100, 10);
```

JavaScriptはオブジェクト内に関数が持てるので、
以下のようにすることもできます。
(著者は使用したことありませんが)

```typescript
// 型の定義：関数が複数存在するケース
interface SomeFunction {
  functionName1(): returnType;
  functionName2(args: argsType): returnType;
}

// 型からオブジェクトを作る
const someFunction: SomeFunction {
  functionName1() {
    return 100;
  };
  functionName2(args) {
    console.log('hoge')
  };
}

// オブジェクト内の関数を使用
someFunction.functionName1();
```

# 3. 関数宣言と同時に型を定義するケース
先ほどは、**先に型を定義して**から関数の宣言をしていましたが、
こちらは、関数宣言と同時に型を定義するケースです。

## 3-1. パターン1
```typescript:定型文
const functionName: (args: argsType) => returnType = (args) => {
  // run process
};
```

```typescript:使用例
const calculate: (price: number, tax: number) => number = (price, tax) => {
  return price * tax;
};

calculate(100, 10);
```

こちらは可読性が悪いので、少し解説を加えます。

functionNameから後の、 `: () => returnType までは型` です。

```typescript
// ここまでが型の定義
const functionName: (args: argsType) => returnType
```

そこから後の、`= () => が実装` になります。

```typescript
// この部分が実装
= (argsValue) => {
  // run process
};
```

2022/01/27追記 
**※ただし、返り値の型を型推論する場合は以下の形になる**

```typescript
const calculate = (price: number, tax: number) => {
  return price * tax;
};

calculate(100, 10);
```

## 3-2. パターン2
※2022/02/21追記
こちらのパターンが漏れていたため、追記致します。
(ご指摘頂き有難うございます)

```typescript
const calculate = (price: number, tax: number): number => {
  return price * tax;
};
```

# まとめ
- 関数の型は色々なケースと書き方が存在する
- 「関数宣言時」は基本的に `:` に続く記述が型
- 「関数宣言時」は基本的に `=>` に続く記述が実装
- **関数宣言と同時に型を宣言**すると、`= =>` が2回登場するので、非常に可読性が悪い
    - ただし、**3-2. パターン2**だと短く記載できる
- 「クラス内」での関数宣言時は、`functionName() ~~` となり、`=` が不要
- 「クラス外」での関数宣言時は、`functionName: FunctionType = ~~` となり `=` が必要

以上となります。
お読み頂き有難うございました。