typescript - ジェネリック

javaScriptやpythonのような動的型付け言語を使っていると、typescriptやJavaのような静的型付け言語を初めて使った時に分からないことが多発しますね。

ということで今回はジェネリックについて理解できたことをまとめます。ジェネリックはデータ型をパラメータ化する仕組みです。山かっこ<>の中に区切って指定します。クラスや関数に定義します。下の例は単純な関数にジェネリックを使用した例です。このように定義するときはデータ型のところを大文字の変数(今回はT)で表します。そのあと実際に使用する時にfnc<string>("hogehoge")というように型を設定します。

function fnc<T>(x: T): T {
   return x;
}
let str: string = fnc<string>("typescript");
alert(str); //typescript
let num: number = fnc<number>(999);
alert(num); //999

このように関数を宣言したいけど引数の型が定まらない時にジェネリックを使います。例えば単純に引数に与えられた変数の中身をコンソールに表示するだけの関数を作りたいとしましょう。typescriptは静的型付け言語なので以下のように全ての型について関数を宣言する必要があります。

function fnca(x: number): void {
   console.log(x);
}
function fncb(x: string): void {
   console.log(x);
}
function fncc(x: boolean): void {
   console.log(x);
}

これは、無駄が多すぎますが型を縛っているため統一することができません。そのため関数を使用する時に型を定義するジェネリックがあるということです。

function fnc<T>(x: T): void{ //返り値がTではないことによるコンパイルエラー
   alert(x);
}
fnc<string>("hogehoge");

ちなみに上では返り値が無いのでそれを表すvoidを付けてますがこれがTと異なるのでコンパイルエラーが出ています。typescriptではジェネリックは引数・返り値共に揃っている必要があるみたいです。

そうなると単純に「文章から文字数を求める」など引数と返り値の型が揃っていない時にどうするんだ!となるかもしれません。しかし引数が定まらないということはどの種類の型にも共通する処理をする関数でなくてはなりません。そうなると引数と返り値は共通にならざるをえないということです。

逆にいうと引数と返り値が異なることが想定できる場合というのは、「文章から文字数を求める」のように具体的な引数の型が想定できる場合でその他の型には処理が適用できない場合でしょう(文字数を求めるのはstring型特有の処理ですよね)。なのでジェネリックを使用する時点で引数と返り値は統一されないとおかしいということです。ちなみにさっきのは以下のようにコードを書くことで問題なく処理されます。anyを使うということは型のチェックを行わないということなので避けるべきですが、使用する時にエラーが起きないことが想定できるならありだということです。

function fncd(x: any): void {
   alert(x);
}

あまり使いどころはなさそうな気がしますが、無い頭を振り絞って出した下のように入力された値を10回配列にいれるコードはコンパイルエラーなく実行できそうです。

function arr<T>(x: T):T[] {
   const ar: T[] = [];
   let i: number;
   for (i = 0; i < 10; i++) {
       ar.push(x);
   }
   return ar;
}

動的型付け言語のぬるま湯に浸かってきたので存在意義すら解らないことが多々ありますね。頑張ろう!!

この記事が気に入ったらサポートをしてみませんか?