TypeScript: オーバーロードメソッドを定義する方法

TypeScriptのオーバーロードメソッドを定義する方法を説明する。

TypeScriptのオーバーロードは、Javaなどの他の言語から見ると特殊な定義の仕方になっている。

## オーバーロードメソッドはインターフェイス部分と実装部分を別々に書く

TypeScriptのオーバーロードはJavaなどの言語とは異なり、メソッドのインターフェイス定義部分と、メソッドの実装部分を分けて書く。

```kotlin
// Kotlinのオーバーロードの例。
// よくある言語のオーバーロードは、インターフェイスと実装がひとまとまりになる書き方だが……
class Twicer {
    fun twice(num: Int): Int {
        return num * 2
    }
    
    fun twice(str: String): String {
        return str + str
    }
    
    fun <T>twice(arr: Array<T>): Array<T> {
        return arr + arr
    }
}
```


```typescript
// TypeScriptのオーバーロードの例。
// TypeScriptはインターフェイスと実装を分けて書く。
class Twicer {
    // オーバーロードメソッドのインターフェイス
    twice(num: number): number // 数を2倍する処理のインターフェイス
    twice(str: string): string // 文字列を2回繰り返す処理のインターフェイス
    twice<T>(arr: T[]): T[]    // 配列要素を2倍にする処理のインターフェイス

    // オーバーロードメソッドの実装
    twice(value: any): any {
        // 1つ目のオーバーロードメソッド twice(num: number): number の処理
        if (typeof value === 'number') {
            return value * 2
        }

        // 2つ目のオーバーロードメソッド twice(str: string): string の処理
        if (typeof value === 'string') {
            return value + value
        }

        // 3つ目のオーバーロードメソッド twice<T>(arr: T[]): T[] の処理
        if (Array.isArray(value)) {
            return [...value, ...value]
        }
    }
}
```

→[このコードをTypeScript Playgroundで動かす](https://www.typescriptlang.org/play/?ssl=15&ssc=1&pln=16&pc=1#code/MYGwhgzhAEAqDuBLYBTATtA3gWAFDQOgBclUAKAOwFcBbALmmpoCN0BKBp1tPQ40lGQhE0DYWkQUA5h2jjJU3oRLIUAHlgA+MmDSi4AbQC6s2MaUEVqDdoBuYEFRQMwFAJ6zXbrBb7REAGbQZERuAA4oAPZB9o4o0AC8SdAA5FzoKWw++H65aChEVGgU0LFO0ABU0ABMvnwAvnWEgcGhEdGlDuVJCany0pnZuXkFRSVl8QDUnXFNBI05uS1kAIJ6YG4AdIgQa2gbZBNsWTiLwwT5hcXQBpt3EwA00HebE0Zz0AsNeAt4wJEUYT8VQYXoUFDwOACNBkNh-AFApicWjcRLA1BoTZWQQARjhuH+gKIchEYhECjR2Mx2LIKTAmXhROgun0-Skxkp0KxAjIBhSiCIKBoKRMeCp3NUITQTiyAHpZdBALwbgGq9wDKDIAYhkA9gyAZoZAM8MgEWGQAlDIBrhkAFQyAS4ZAD8MeEZEEiIBQmxAkSkZEwjFoT3ETxZnzYQA)

このTSコードをコンパイルしたJavaScriptコードは次のようになり、当然ながらオーバーロードメソッドのインターフェイス部分は消される:

```javascript
// コンパイル後のJavaScriptコード
class Twicer {
    twice(value) {
        if (typeof value === 'number') {
            return value * 2;
        }
        if (typeof value === 'string') {
            return value + value;
        }
        return [...value, ...value];
    }
}
```

オーバーロードメソッドの使い方はJavaなど他の言語と同様に、メソッド名は同じで引数の型だけ違う呼び出しができる:

```typescript
const twicer = new Twicer()
const num: number = twicer.twice(1)
const str: string = twicer.twice('a')
const arr: string[] = twicer.twice(['item'])

console.log({ num, str, arr })
// Output:
// { num: 2, str: 'aa', arr: ['item', 'item'] }
```

もちろん、オーバーロードメソッドにないインターフェイスはコンパイルエラーになる:

```typescript
twicer.twice(true) // 🚫boolを2倍するインターフェイスは無いのでコンパイルエラーになる
```

最初にあげたTypeScriptのオーバーロードメソッドの実装例では、引数の型と戻り値の型が`any`になっていることに気がついたと思う。これは、`number`, `string`, `T[]`をすべて扱えるようにするためだ。もちろんここにユニオン型を使うと`any`を避けることができる。

```typescript
class Twicer {
    twice(num: number): number
    twice(str: string): string
    twice<T>(arr: T[]): T[]
    twice<T>(value: number | string | T[]): number | string | T[] {
        if (typeof value === 'number') {
            return value * 2
        }
        if (typeof value === 'string') {
            return value + value
        }
        return [...value, ...value]
    }
}
```

→[このコードをTypeScript Playgroundで動かす](https://www.typescriptlang.org/play/#code/MYGwhgzhAEAqDuBLYBTATtA3gWAFDQOgBclUAKAOwFcBbALmmpoCN0BKBp1tPQ40lGQhE0DYWkQUA5h2jjJU3oRLIUAHlgA+MmDSi4AbQC6s2MaUEVqDdoBuYEFRSda3aAB85IhR8MmXLOi+8tK+ZkZYFnzQiABm0GREAJ4ADigA9vH2jijQALwF0ADkXOhFbJH40dVoKERUaBTQ2U7QAFTQAExRfAC+PYRxCclpmc0OrQV5xSFS5ZXVNXUNTS25ANTjOQME-VVL9Y3QBgB0Z2sANNBnJ2tGUXt7eMDpFML8qhjTFCjwcAJoMhsZ6vd5MAJuaZWdAnaFkACMwNwLzeRC8+lm+Q+qDQsIEZCKYHKINR0F0GO80mMWOhuLhBiKiCIKBoRRMeFpeNUiTQTgqAHp+dBALwbgGq9wDKDIAYhkA9gyAZoZAM8MgEWGQAlDIBrhkAFQyAS4ZAD8MeBJEHSIBQJxA6SkZEwjFoV3EV3J0F6bCAA)

この場合、1つ目のif分岐の次からは`value: string | T[]`が推論され、2つ目のif分岐の次からは`value: T[]`が推論されるため、3つ目の`if(Array.isArray(value))`を書く必要がなくなる。

## 次に読むといいかもしれない

https://qiita.com/suin/items/2653fea448fd6246437a