TypeScriptの総称型(Generic Type)

目次

JavaScript学習のついでに、TypeScriptも学んでみます。

今日は、TypeScriptの「総称型」という機能について調べてみました。

総称型とは?

総称型 – Wikipedia

総称型(generic type)、あるいはパラメタ付型(parametric type)とは、型付けされたプログラミング言語においてデータ型の定義とそれを参照する式(型式)の一部にパラメタを許すことによって類似した構造を持つ複数のデータ型を一括して定義して、それらを選択利用する仕組みである。

総称型は、暗黙の型変換(implicit type conversion)あるいは型強制(type coercion)、多重定義あるいはオーバーロード(overload)、継承(inheritance)あるいは包含(inclusion)と並んでプログラミング言語においてポリモーフィズムを実現するための一つの手段であると看做せる。

総称型が使われている言語の例としてC++のテンプレート、JavaやC#のジェネリクスがある。

「総称型 TypeScript」で検索したら、分かりやすい説明がありました。

総称型 TypeScript – Google 検索

総称型の仕組み

プログラミング初心者向けのTypeScript入門連載。第11回は関数のさまざまな使い方について詳しく解説する。TypeScriptでプログラミングへの理解を一歩深めよう。
第11回 関数に関するいくつかのトピック (3/4) - @IT

ジェネリックスとは、データ型を仮に決めておき、実際に使用するデータ型を呼び出し時に変えられるようにする機能で、総称型とも呼ばれる。

使用するデータ型(数値型、文字列型など)を、後で決めたい/その都度変えたい場合に便利な機能ですね!

ジェネリックスは、さまざまなデータ型の引数に対してきめ細かく処理を分けるためではなく、どのデータ型に対しても同じような処理をしたい場合に使う

例えば関数(メソッド)で、いろんなデータ型に対応させたい場合、「オーバーロード」という仕組みもありますが、何か違うのでしょうか?

型引数とは?

TypeScript(プレビュー版)機能解説。JavaScriptのスーパーセットである「TypeScript」。その言語機能として追加されたものの中で、特に注目度が高い「ジェネリック(Generics)」の言語仕様や機能内容を紹介。
TypeScriptの目玉機能「ジェネリック(Generics)」はこうなっている - Build Insider

ジェネリックは簡単に言えば、型引数を使用して、実際に利用されるまで型が確定しないクラスや関数を実現するためのものだ。

型の情報(種類)を入れておく変数

型(type)の種類を入れておく変数を「型変数」(type variable)と呼びます。

型の情報を、引数(parameter)として渡すときの型変数を型引数と呼びます。

→「型変数」「型引数」「型パラメーター」など、いろんな用語が出てきますが、要するに、型の種類を入れておく「箱」(変数)が使われるってことですね?

ジェネリクス | TypeScript 日本語ハンドブック | js STUDIO

「値」ではなく「型」上で動作する特別な種類の変数である、「型変数 (type variable)」

用語

  • 型引数(type parameter)は、「<T>」というような記号で書きます。
  • 「<>」はダイヤモンド演算子と呼ばれています。
  • TypeScriptの場合、<string>は文字列型、<number>は数値型になります。
  • <T>は仮型引数、<string>などは実型引数と呼ばれています。

総称型とオーバーロードの違い

総称型のありがたみ、登場の経緯が分かる事例が紹介されていたのでメモ。

(1)似た機能の関数がバラバラに用意されている状態

同じような機能を持つ2つの関数のコード

function alertString() {
  alert("ABC");
}
 
function alertNumber() {
  alert("123");
}
 
alertString();
alertNumber();

上記の関数は、値が違うだけで文字列を出力するという機能は同じです。

これらを1つにまとめるには、「引数」を使用します。

(2)引数を使って似た機能をまとめた関数

上記の2つの関数を、string型の引数「x」を使用して1つの関数にまとめた例

function a(x: string) {
  alert(x);
}
 
a("ABC");
a("123");

(3)引数のデータ型が違うけど、機能が似ている関数

string型の引数を持つ関数と、number型の引数を持つ関数

function a(x: string) {
  alert(x);
}
 
function b(x: number) {
  alert(x);
}
 
a("ABC"); // string型のデータ
b(123);   // number型のデータ

引数のデータ型が異なる2つの関数を1つにまとめたいときは、どうすれば良いでしょうか?

(4)「Any型」で全部の型を引き受けられる関数

上記の2つの関数を、Any型の引数「x」を使用して1つの関数にまとめた例

function a(x: any) {
  alert(x);
}
 
a("ABC");
a(123);

Any型は、文字列型でも数値型でも、どんなデータ型も入れられる型です。

しかし、Any型は「型チェックの放棄」を意味するので、できれば使いたくはありません。

(5)オーバーロードで型チェックを実現した関数

オーバーロードを使用して引数の型を限定した例

function a(x: string); // string型のシグネチャー
function a(x: number); // number型のシグネチャー
function a(x: any) {   // オーバーロードされた関数の実装
  alert(x);
}
 
a("ABC");
a(123);

「オーバーロード」(多重定義)という仕組みを使って、引数のデータ型がstring型とnumber型の場合のそれぞれに対応可能となりました。

オーバーロード(多重定義)とは?

オーバーロードとは|overload : 意味/定義 – IT用語辞典

プログラミングの分野で、同名の複数の関数やメソッド、演算子などを定義し、引数や被演算子の数やデータ型などに応じて使い分けることができる仕組みのことをオーバーロードという。「多重定義」と訳される。

関数やメソッドのオーバーロードでは、処理内容が同じか似ているが、引数の型や数が異なる同名の関数やメソッドを複数定義することができ、呼び出し側の引数の記述に応じて、対応するものが選択されて呼び出される。

シグネチャーとは?

シグネチャ[シグニチャ]とは:SJC-P対策Java用語集

シグネチャとは、メソッドの名前、およびそのメソッドに対する 引数の数と型により構成されています。

つまり、次の3つを組み合わせたものをシグネチャと呼びます。

  1. メソッド名
  2. 引数の数
  3. 引数の型

上記の例では、メソッド名「a」、引数名「x」は同じですが、引数の型が違うので、2つのシグネチャーを用意しました。

もっと他の型も扱えるようにしたい場合は、引数の型をその都度設定できる仕組みがあると便利です。

「型引数」を導入して、「総称型」という仕組みを使うと、オーバーロードでたくさん用意した関数を1つにまとめられます。

(6)総称型でオーバーロードした関数をまとめる

型引数を導入して、総称型でまとめた例

function a<T>(x: T) {
  alert(x);
}
 
a<string>("ABC");
a<number>(123);

「a<string>」の引数は文字列だけ、「a<number>」の引数は数値だけが受け付けられます。

もっと他の型も利用可能で、「a<Date>」などと書けば日付時刻も受け付けられます。

  • オーバーロードでは、最初に用意したシグネチャー以外は受け付けられません。
  • 総称型では、その都度型を指定すれば良いので、柔軟に対応させられます。

以上が、関数(メソッド)で総称型を使用した事例です。

型引数は、クラスやインターフェースにも使えます。

(詳細は上記のページをご参照ください。)

総称型の使いどころ

プログラムが大きくなって、複雑になってくると、

「似たようなコードをまとめてコードをコンパクトにしたい」

という場合があります。

「この2つの機能は、型を除けばそっくりな処理を行っている」という場合、型の情報(データ型)を抽象的に扱う機能が必要があれば便利ですね?

そんなときこそ、「総称型」の出番です!

総称型(ジェネリック)の特徴

  • ジェネリックは、使用されるまで不明の任意の型を利用する技術
  • 型引数が、任意の型に対応する
  • 値が変化するときは引数。型が変化する時は型引数で関数をまとめられる
  • 引数は関数に付けるものだが、型引数はクラスやインターフェースにも付けられる
  • 型引数は複数あってよい
  • 型引数は、引数、戻り値の型指定、関数やクラスの内部にも使用できる
  • 型引数の型はコンパイラが推論してくれるが、頼りすぎは禁物
  • 型には制約を付けることができる
  • ジェネリックは、コレクションと相性がよい
  • 一般的に、コードサイズが大きくなっていくと、ジェネリックの出番が増えていく

リファクタリングとは?

JavaScript学習に役立つお話がありました。 「リファクタリング」でプログラムを改善する練習について紹介されています。 www.webprofessional.jp (前回に引き続き、今回もこのお話から学んでみたいと思います。) jsstudy.hatenablog.com アプリ作成の進め方として、以下の手順が紹介されています。 最初に基本を身に着けよう 計画を立てる コード無しで書いていく 小さな部分に分けて製作する 各パーツを結合する 実験とテスト 外部の助けを求める コードのリファクタリング(再構築) リファクタリングとは? reの意味 - 英和辞典 ...
リファクタリング=読みやすいプログラムを書こう! - JavaScript勉強会 - JavaScript勉強会

リファクタリング (refactoring) とは、コンピュータプログラミングにおいて、プログラムの外部から見た動作を変えずにソースコードの内部構造を整理することである。

似たような関数が増えたときは、総称型を活用してみたいと思います。

[amazonjs asin=”4822296539″ locale=”JP” title=”Angular2によるモダンWeb開発 TypeScriptを使った基本プログラミング”]

投稿日:April 17th 2017

元記事:http://jsstudy.hatenablog.com/entry/typescript-generic-type

– PR –