KAKUTA TECH BLOG

Blog Article

TypeScriptの関数のオーバーロードとは?

はじめに

TypeScriptを使っていると「関数のオーバーロード」という言葉を目にすることがあります。

でも、「オーバーロードって何?」「何のために使うの?」と疑問に思う方も多いのではないでしょうか?

この記事では、プログラミング初心者にもわかりやすく、TypeScriptにおけるオーバーロードの基本やメリット、使いどころを解説していきます。

オーバーロードとは?

関数のオーバーロード(overload)とは、同じ関数名に対して、引数の型や数などが異なる複数の呼び出し方を定義する仕組みです。

例えば、次のような関数を考えてみましょう。

function processValue(value: number): string;
function processValue(value: string): number;

この場合、processValue() は、

  • number を渡すと string を返す
  • string を渡すと number を返す

という2通りの使い方ができることを、TypeScriptに伝えています。

実際の処理は、1つの関数にまとめて記述します。

function processValue(value: number): string;
function processValue(value: string): number;

function processValue(value: number | string): string | number {
  if (typeof value === "number") {
    return `数値: ${value}`;
  }

  return value.length;
}

このように、オーバーロードでは呼び出し側に見せる型のパターンを複数定義し、実際の処理は1つの関数にまとめることができます。

オーバーロードを使うメリット

オーバーロードを使うと、関数がどのような引数を受け取り、何を返すのかを明確にできます。

例えば、先ほどの関数は次のように呼び出せます。

const result1 = processValue(123);
// string

const result2 = processValue("Hello");
// number

TypeScriptは、渡した引数に応じて戻り値の型も判断してくれます。

const result1 = processValue(123);
result1.toUpperCase(); // OK

const result2 = processValue("Hello");
result2.toFixed(); // OK

このように、引数と戻り値の関係を明確にできることがオーバーロードの大きなメリットです。

また、エディタの補完でも、定義した呼び出し方に応じた型情報が表示されるため、関数の使い方を理解しやすくなります。

シグネチャとは

関数が「どんな引数を受け取り、何を返すのか」を表したものくらいに考えればOKです。

function add(a: number, b: number): number {
  return a + b;
}

この関数なら、

add(number, number) → number

という情報が、その関数のシグネチャです。

つまり、

  • 関数名:add
  • 引数:number、number
  • 戻り値:number

という関数の型に関する情報です。

オーバーロードの書き方

TypeScriptのオーバーロードは、基本的に次の形で記述します。

// オーバーロードシグネチャ
function 関数名(引数: 型): 戻り値の型;
function 関数名(引数: 型): 戻り値の型;

// 実装
function 関数名(引数: 型): 戻り値の型 {
  // 処理
}

最初に複数のオーバーロードシグネチャを記述し、その下に実際の処理を行う実装シグネチャを記述します。それぞれの役割は、以下です。

種類

役割

オーバーロードシグネチャ

外から見た「使い方」の定義

実装シグネチャ

実際の処理を行うための定義

例えば、

function processValue(value: number): string;
function processValue(value: string): number;

function processValue(value: number | string): string | number {
  if (typeof value === "number") {
    return `数値: ${value}`;
  }

  return value.length;
}

ここでは、上の2つがオーバーロードシグネチャです。

function processValue(value: number): string;
function processValue(value: string): number;

そして、下の関数が実際の処理を行う実装です。

function processValue(value: number | string): string | number {
  // ...
}

オーバーロードが役立つ場面

オーバーロードは、同じ関数に複数の自然な使い方があり、それぞれの引数と戻り値の関係を明確にしたい場合に役立ちます。

例えば、次のようなケースです。

シチュエーション

オーバーロードを使う理由

引数の型によって戻り値の型が変わる

引数と戻り値の関係を明確にできる

引数の数によって処理方法が変わる

複数の呼び出し方を1つの関数として定義できる

ライブラリやAPIを作る

利用者に関数の使い方をわかりやすく伝えられる

複数の呼び出しパターンを提供したい

呼び出し側で適切な型チェックや補完を受けられる

オーバーロードを使わない場合との違い

例えば、オーバーロードを使わずに次のように書くこともできます。

function processValue(value: number | string): string | number {
  if (typeof value === "number") {
    return `数値: ${value}`;
  }

  return value.length;
}

これでも関数自体は動作します。

しかし、呼び出し側から見ると、

const result = processValue(123);

の result は、

string | number

と判断されます。

つまり、実際には number を渡した場合は string が返ってくるにもかかわらず、TypeScriptからは「string または number のどちらか」としか判断できません。

一方、オーバーロードを使うと、

function processValue(value: number): string;
function processValue(value: string): number;

と定義できるため、

const result = processValue(123);
// string

のように、引数に応じた戻り値の型を正確に表現できます。

まとめ

項目

内容

何か?

同じ関数名に対して、引数の型や数などが異なる複数の呼び出し方を定義する仕組み

使う理由

1つの実装で複数の呼び出し方に対応し、それぞれの型の関係を明確にするため

いつ使う?

引数によって戻り値の型などが変わり、その関係を明確にしたいとき

メリット

型チェックやエディタ補完が正確になり、関数の使い方もわかりやすくなる

最後に

オーバーロードは、「1つの関数に複数の呼び出し方を定義する仕組み」と考えると理解しやすいです。特に、「この引数ならこの戻り値」という関係をTypeScriptに正確に伝えたい場合に役立ちます。

ただし、すべての関数で使う必要があるわけではありません。

単純な処理であれば union型 などで十分な場合もあるため、複数の呼び出し方を提供することで型情報がわかりやすくなる場合に使うとよいでしょう。