[TypeScript] 関数の型定義をマスターしよう！

### はじめに
前回に続き、関数の利用についてまとめました。

### 【目次】
[まずは基本的な型宣言をマスターしよう！](https://qiita.com/PenPe/items/c189a611b3407b7ceef9)
[オブジェクト型の型宣言をマスターしよう！](https://qiita.com/PenPe/items/440683cc6e5e4ac0ca32)
[関数の型定義をマスターしよう！](https://qiita.com/PenPe/items/bff109c6f83f46d191a7) ←🈁
[配列をマスターしよう！](https://qiita.com/PenPe/items/7e056eec2cce76a745a9)
[インターフェースをマスターしよう！](https://qiita.com/PenPe/items/f9314aeccb2f23cac382)
[クラスを使いこなそう！](https://qiita.com/PenPe/items/c8e6289f386c4db23a6f)
[型修飾子についての理解を深めよう！](https://qiita.com/PenPe/items/7af57861e93830fe9a79)
[ジェネリックをマスターしよう！](https://qiita.com/PenPe/items/fde6a342e746e265d907)
[独自の構文拡張をマスターしよう！](https://qiita.com/PenPe/items/c79cdb5bc4a68a1dfd7f)

---

### 関数に関する前提知識
JavaScriptとTypeScriptでは以下の違いがあります。
- JavaScriptは引数の数が任意でも呼び出し可
    - 引数が`undefind`を取り得る
- TypeScriptは引数の数を厳密にチェックしている
    - 引数の数が異なる場合、型エラーが発生する

#### JavaScript
```javascript
function fn (input1, input2) {
  console.log(input1, input2)
}

fn("hoge")

// [LOG]: "hoge",  undefined 
```

#### TypeScript
```typescript
function fn (input1: string, input2: string) {
  console.log(input1, input2)
}

fn("hoge") //NG: Expected 2 arguments, but got 1.
```

---

### 引数の型アサーション
`引数名: 型`とすることで引数の型定義が可能です。

```typescript
function fn(input: string) {
  console.log(input.toLocaleUpperCase)
}

// OK
fn("hoge")

// NG: Argument of type 'number' is not assignable to parameter of type 'string'.
fn(1234)
```

---

### オプションパラメータ
引数をオプショナル（省略可能）としたい場合は引数名の末尾に `?` をつけることで、TypeScriptにオプショナルな引数であることを伝えることができます。
```typescript
function fn (input1: string, input2?: string) {
  console.log(input1, input2)
}

//OK: "hoge",  undefined 
fn("hoge")

//OK: "hoge",  "foo" 
fn("hoge", "foo")
```

---

### デフォルトパタメータ
`引数名: 型 = デフォルト値`とすることで、引数に対してデフォルト値を設定することができます。

```typescript
function fn (input1: string, input2:string = "foo" ) {
  console.log(input1, input2)
}

//OK: "hoge",  "foo" 
fn("hoge")

//OK: "hoge",  "buz" 
fn("hoge", "buz)
```

---

### レストパラメータ
引数として、任意の長さの配列を受け取りたい場合はレストパラメータ（`...引数`）を使用する。
レストパラメータを用いる場合は、そのパラメータが取りうる`データ型[]`を型指定する。

```typescript
function fn (...inputs: string[]) {
  for(let input of inputs) {
    console.log(input)
  }
}

//OK: "hoge"
fn("hoge")

//OK: "hoge", "foo", "buz"
fn("hoge", "foo", "buz")
```

---

### 戻り値の型
パラメータの後に `: 型`を指定することで、関数の戻り値を定義できますl
```typescript
// OK
function fn (input: string): number { 
  console.log(`called fn with ${input}`)
  
  return input.length
}

// NG: A function whose declared type is neither 'undefined', 'void', nor 'any' must return a value.
function fn (input: string): number {
  console.log(`called fn with ${input}`)
}

```

---

### 関数の型宣言
JavaScriptでは、関数を値として渡すことができます。
TypeScriptでは、関数に対してデータ型を定義することができます。

```typescript
// 引数なし: string型の戻り値を返す
let nothingInputFn: () => string;

// string型の引数を1つ受け取り、number型の戻り値を返す
let stringInputFn: (input: string) => number
```

これらはコールバック関数を表す際によく利用されます。

```typescript
const songs = ["Hello", "MyName", "is", "Code", "Taro"]

function runSongs(phrases: string[], song: (lyrics: string) => string) {
    for(let phrase of phrases) {
        console.log(song(phrase))
    }
}

// (lyrics: string) => string に適合
function songAt(lyrics: string) {
    return `${lyrics}~`;
}

runSongs(songs, songAt);

// "Hello~", "MyName~", "is~", "Code~", "Taro~" 
```

---

### 戻り値の型
特徴的な戻り値のデータ型についてご紹介します。
#### void
戻り値がないことを示す。

```typescript
function fnA(input: string): void {
    console.log(input);
}

// voidの戻り値は変数に格納できない（戻り値を利用できない）
let buz: undefined = fnA();
```

#### never
関数の実行後に呼び出し側のコードが実行されないことを意味する（例外処理などに利用）
```typescript
function throwException(message: string): never {
    throw new Error(message);
}

console.log("hello")
throwException("エラー発生。");

// ↓以降の処理は実行されない
console.log("World")
```

---

### オーバーロードシグネチャ
TypeScriptの機能で、ひとつの関数に異なる関数シグネチャを複数もつ関数です。
異なる引数や戻り値のパターンがいくつかある関数をオーバーロード関数と呼びます。

#### 前提
TypeScriptでは、オーバーロード関数は、関数シグネチャと実装シグネチャの2つの部分に分けて書きます。
- 関数シグネチャ
    - TypeScriptが関数の呼び出しが正しいかチェックするための関数呼び出しパターン
- 実装シグネチャ
    - 内部ロジック
    - 関数シグネチャと互換性がある形で実装する必要がある。

#### 基本的な宣言
```typescript
function 関数名 関数シグネチャ1
function 関数名 関数シグネチャ2
function 関数名 すべてのシグネチャを網羅する実装シグネチャ
```
最後の1行を除く、1, 2行目が関数シグネチャ、
最後の1行が実装ということだけ覚えて読み進めてください。

#### サンプルコード
```typescript
function hello(person: string): void;
function hello(persons: string[]): void;
function hello(person: string | string[]): void {
  if (typeof person === "string") {
    console.log(`Hello ${person}`);
  } else {
    console.log(`Hello ${person.join(",")}`);
  }
}
```

実装シグネチャを見ると、全ての関数シグネチャと互換性がある実装となっていますね。
- `(person: string): void;`
- `(persons: string[]): void;`
どちらの形で引数を渡しても問題なく実行できる実装となっている。

<br>

他の項目と違って初学者の方は難しいかもしれません。
経験則ですが、規模が大きいプロジェクトなどで複雑な関数を定義しない限りは利用する機会は少ないのではないでしょうか？

ref. 

https://typescriptbook.jp/reference/functions/overload-functions

---

### TypeScriptにおけるアロー関数
:memo: 2024/4/25 追記

後々気がついたのですが、
`TypeScript`では、アロー関数を型アノテーションで記述する場合、引数が1つであってもカッコを省略することができないようです。

#### javascript
```javascript
let sampleFn = input => console.log(input);
sampleFn("hoge");
```

#### typescript
```typescript
// NG:
interface Foo {
    fn: input:string => void
}

// OK:
interface Foo {
    fn: (input:string) => void
}
```

他にもびっくりされる方がいそうなので追記しました。

---

以上です！
Ruby開発が多いので、やっぱり静的型付けっていいよなと思い始めたこの頃...

次節で配列に関する記事を公開しておりますので、よろしければご覧ください🙏

https://qiita.com/PenPe/items/7e056eec2cce76a745a9