Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

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

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

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

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

// 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はインターフェイスと実装を分けて書く。
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で動かす

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

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

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

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'] }

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

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

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

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で動かす

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

suin
Qiita 4位/TypeScript入門書執筆中/TypeScripterのための座談会「YYTypeScript」主催/『実践ドメイン駆動設計』書籍邦訳レビュア/分報Slack考案/YYPHP主催/CodeIQマガジン執筆/株式会社クラフトマンソフトウェア創設/Web自動テスト「ShouldBee」の開発/TypeScript/DDD/OOP
https://yyts.connpass.com/
craftsman_software
「インフラの心配は、もうおしまい」 インフラ運用を自動化し、手作業を限りなくゼロにする会社
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away