👀

型変数と型引数【個人学習まとめ】

に公開

型変数とは

端的に言えば型を代入する箱。

「変数」といえば、値を代入する箱である。という表現を使われることが多いです。

変数はを代入できますが、型変数はを代入することができます。

//変数の使用例
const hensu = "変数です!";
console.log(hensu);
console.log(hensu.length);

//型変数の使用例
function sampleFunction<KataHensu>(value: KataHensu): KataHensu {
  console.log(value);
  return value;
}

上記の例では、変数と型変数それぞれの使用例です。(変数の方はもう知ってるよ!という方が多いかと思いますが、比較のために用意しました。)

変数hensuは、一度定義してしまえば値を書き写す必要もなくなりますね。また、コンソールを使って変数に代入されている値を画面に出力したり、lengthプロパティを使って代入されている値の長さを取得することもできます。

型変数KataHensuは、一度定義してしまえば関数sampleFunction内では一貫して参照可能な型として機能します。
型変数を定義する場合は、変数名を<>で囲むことによって定義します。上記の例だと<KataHensu>が型変数KataHensuを定義している部分になります。

さらに、関数sampleFunctionの引数valueと戻り値にも型変数KataHensuが利用されていますね。これが一つ上で説明した「関数sampleFunction内では一貫して参照可能な型として機能します」
に該当します。
つまり、型変数KataHensuのスコープは関数sampleFunctionの範囲になります。仮に関数sampleFunctionの外で利用しようとしてもエラーとなります。

function sampleFunction<KataHensu>(value: KataHensu): KataHensu {
  console.log(value);
  return value;
}

+ let errorVarlue: KataHensu;
→ 名前 'KataHensu' が見つかりません。

ここまでで、型変数の名前をKataHensuとしていました。
しかし、TypeScirpt の慣習としてはTという単一の大文字が使用されます。型変数が 2 つある場合は 2 つ目にUを、型変数が 3 つある場合は 3 つ目にVが利用されます。(今回のようにKataHensuという名前を付けることもできます。逆に利用できないのはclassconstなどの予約語です。)

型引数とは

型変数に代入した型のことです。

const sampleValue = sampleFunction<string>("サンプルコードです");

上記のサンプルだとstringが型引数になります。
型引数を明示的に指定しましたが、変数の"サンプルコードです"から TypeScript 側が変数の型がstringであると型推論を行ってくれるので、次のように型引数を省略することも可能です。

const sampleValue2 = sampleFunction("サンプルコードです");

型引数のデフォルト値

「型引数を省略することも可能だ」ということを説明しました。
しかし、型引数が渡されなかった場合やundefinedが渡された場合はどうでしょうか?関数の呼び出し自体は問題なくできますが、関数内部の処理ではエラーが発生してしまう可能性があります。
これを避けるためにも、型引数にデフォルト値を設定することができます。

function createPair<T = number, U = string>(first: T, second: U): [T, U] {
  return [first, second];
}

上記の例では、関数createPairに型変数TUを設定しています。Tのデフォルト値としてnumber型を、Uにはstring型を指定しています。デフォルト値を指定する時は=で結びます。
関数の引数と戻り値は型変数TUをそれぞれ参照しています。

仮に戻り値の順序を入れ替えるとどうでしょうか?

function createPair<T = number, U = string>(first: T, second: U): [T, U] {
-  return [first, second];
+  return [second, first ];
→ 型 'U' を型 'T' に割り当てることはできません。
  'U' に関連しない可能性のある任意の型で 'T' をインスタンス化できます。
}

型の不一致でエラーとなりました。型引数が十分に機能していることが分かるかと思います。

この関数createPairを呼び出す時は通常の関数を呼び出すように記述して OK です。

const numAndStr = createPair(123, "あいう");[ 123, 'あいう' ]
const strPair = createPair("苗字", "名前");[ '苗字', '名前' ]

引数のデフォルト値と関数呼び出し時の引数が異なっている場合は、関数呼び出し時にしていている引数の型を優先します。
上記の場合だと、デフォルト値は

  • T = number
  • U = string
    です。
    しかし、変数strPairはどちらもstring型です。この場合はstring型が優先されます。
    型引数のデフォルト値はあくまでも、型情報が不明確な場合に利用されます。

まとめ

型変数は型を代入する箱。
型引数は、型変数に代入した型。

余談

Reactのサンプルコードを見ていると型引数のTUは頻繁に出てきますね。なんとなーくの理解で進めていた部分があるので今回でしっかり学習できました。
しかしまぁ・・・慣れていないとTだけ記述されていても分からんなぁ・・・

Discussion