Typescript(関数オーバーロード)

はじめに

javascriptでは、出力される型が入力される引数の型によって変わることがあります。Typescriptではオーバーロード(引数や戻り値が異なるが名称が同一な複数のメソッドを複数定義する)された関数の宣言と、入力値によって変わる関数の出力型を静的な型システムを使うことで具体化します。

関数オーバーロードによるコードの改善

type Combinable = number | string; //型エイリアス
function combine(input1: Combinable, input2: Combinable) {
  if (typeof input1 === "string" || typeof input2 === "string") {
    return input1.toString() + input2.toString();
  }
  return input1 + input2;
}

コードを解説すると、combineという関数があり、仮引数にはnumber型かstring型のユニオン型を型エイリアスを用いた記述をしています。またif文の中ではinput1,input2のどちらか一つでもstringが実引数に渡れば、文字列で返す(toString())ことを意味しています。そうでなければ(input1,input2のどちらもstringではない)、number型で返すというコードです。
実際に関数を呼び出してみます。

const result1 = combine(1, 2);
console.log(result1);//3

const result2 = combine("ROUND", 1)
console.log(result2);//ROUND1

実際に機能しています。しかし、次のようにドット記法ででメソッドをつなぐとエラーが発生します。

const result1 = combine(1, 2);
console.log(result1.toFixed(2)); //エラー

const result2 = combine("ROUND", 1);
console.log(result2.split('')); //エラー

これはresult1やresult2の型に問題があります。resut1にマウスホバーしてみると
f:id:Tsuboi99553758:20200902102905p:plain
このようにCombinableです。これにより数値を小数含めた形で返すtoFixedメソッドや文字列を一文字ずつ配列で返すsplitメソッドは、resultの型をstring型かnumber型どちらかに型を絞り込むことができていないため使用しようとするとエラーが発生します。
ここでの改善策で関数オーバーロードを用います。

type Combinable = number | string;

function combine(input1: number, input2: number): number;
function combine(input1: number, input2: string): string;
function combine(input1: string, input2: number): string;
function combine(input1: string, input2: string): string;
function combine(input1: Combinable, input2: Combinable) {
  if (typeof input1 === "string" || typeof input2 === "string") {
    return input1.toString() + input2.toString();
  }
  return input1 + input2;
}

const result1 = combine(1, 2);
console.log(result1.toFixed(2)); //3.00

const result2 = combine("ROUND", 1);
console.log(result2.split("")); //(6) ["R", "O", "U", "N", "D", "1"]

これは正しく動作します。なぜなら、result1やresult2の型が先程とは違い、一つ(numberかstring)に絞られているからです。
f:id:Tsuboi99553758:20200902104833p:plain
このように関数を定義する前に記述している4つの場合分けを関数オーバーロードといいます。関数型を定義するように、直感的でわかりやすいです。ここでは4つの関数オーバーロードを定義していますが、必要ない場合分けがあれば省略可能です。