読者です 読者をやめる 読者になる 読者になる

JavaScript勉強会

JavaScriptの学習日記

TypeScriptの総称型(Generic Type)

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

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

 

 

オンラインのTypeScript実行環境

ブラウザー上で、TypeScriptの動作を試せるWebサービスがあります。

www.typescriptlang.org

 

総称型とは?

総称型 - Wikipedia

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

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

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

 

f:id:jsstudy:20170417223525j:plain

 

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

総称型 TypeScript - Google 検索

 

総称型の仕組み

www.atmarkit.co.jp

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

 

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

 

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

 

関数(メソッド)で、いろんなデータ型に対応させたい場合、「多重定義」(オーバーロード)という仕組みもありますが、「総称型」(ジェネリック)とどこが違うのでしょうか?

 

型引数とは?

www.buildinsider.net

 

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

 

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

型(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型の場合のそれぞれに対応可能となりました。

→ a()という1つの関数で、string型とnumber型の引数を受け付けられます。

(「引数の型が違うよ!」というエラーが出ない)

 

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

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

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

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

 

シグネチャーとは?

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

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

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

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

 

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

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

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

 

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

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

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

 

<T>という部分が型引数です。

引数が文字列型のときは「a<string>」、引数が数値型のときは「a<number>」と書いて指定します。

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

 

間違えて、a<number>("ABC");と書いてしまったら、型引数<number>と引数の型("ABC"はstring型)の組合せが合っていないので、コンパイルエラーになります。(記述のミスに気づける)

Argument of type '"ABC"' is not assignable to parameter of type 'number'.

 

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

 

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

 

総称型の使いどころ

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

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

という場合があります。

 

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

そんなときこそ、「型引数」<T>の出番です!

 

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

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

 

リファクタリングとは?

jsstudy.hatenablog.com

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

 

まとめ

「型引数」を使うと、型の取り扱いが柔軟にできるんですね!

似たような機能が増えたときは、「総称型」を活用して、リファクタリングしてみたいと思います。

 

Angular2によるモダンWeb開発 TypeScriptを使った基本プログラミング

Angular2によるモダンWeb開発 TypeScriptを使った基本プログラミング

 

 

オマケ

オーバーロード」というと…なぜかガンダムを思い出しますw(※ネタバレ注意)

アクシズ落下阻止 by M-M アニメ/動画 - ニコニコ動画

逆襲のシャア

アムロ「たかが石ころ一つ、ガンダムで押し出してやる!」

シャア「正気か!?」

アムロνガンダムは伊達じゃない!!」

連邦兵「ロンドベルだけにいい思いはさせませんよ!」

アムロ「駄目だ!摩擦熱とオーバーロードで自爆するだけだぞ!」

シャア「人の意志が集中しすぎて、オーバーロードしているのか?.

    な何、恐怖は感じない? むしろ暖かくて、安心を感じるとは…」

 

オーバーロードのイメージ図(違)

f:id:jsstudy:20170418072028j:plain

プログラミングで行き詰ったときは、応用が利くセリフかも?

「たかがプログラム1つ、●●●●で押し出してやる!」(←ツール名などを入れる)

「▲▲▲▲は伊達じゃない!!」(←プログラミング言語名などを入れる)

ついでにコスプレもすると効果がアップ!?(・∀・) 

 

機動戦士ガンダム シャア コスチューム セット メンズ [公式]

機動戦士ガンダム シャア コスチューム セット メンズ [公式]