今回は、引数や返り値などの型を任意に設定できる型指定方法「ジェネリクス(Generics)」について解説したいと思います。TypeScript以外でもジェネリクスの概念は使用されていますが、慣れていない方にとっては難しい概念だと思います。ぜひこの機会に少しでも理解を深めていきましょう。
ジェネリクス(Generics)とは
ジェネリクス(Generics)とは、引数や返り値などの型を任意に設定できる型指定方法のことを指します。JavaやC#といった別言語でもジェネリクスの機能を使うことができます。
「引数や返り値などの型を任意に設定できる型指定方法」と言われても腑に落ちないと思うので、実際のコードでも確認してみましょう。
まずは型が異なる、似た2つの関数(method1とmethod2)を定義します。
function method1(value: number): number {
return value;
}
function method2(value: string): string {
return value;
}
ここで皆さんはこう思うと思います。
「似たような関数なのでできるだけ1つにまとめたい。」
僕もそう思います。その場合に使えるのがジェネリクスという概念です。
以下がジェネリクスを使い、メソッドを1つにまとめた場合のコードになります。<T>は実行時に何の型に対しての処理かを指定しており、「(value: T): T」の2つのTは型を指定する部分になります。
function method<T>(value: T): T {
return value;
}
では上記のメソッドを呼び出してみましょう。number型の時は数値が、string型の時は文字列がきちんと出力されていますね。
function method<T>(value: T): T {
return value;
}
console.log(method<number>(10));
=> 10
console.log(method<string>("バナナ"));
=> バナナ
このようにジェネリクスの概念を使うことで、どんな型にも対応したメソッドを作ることができます。
配列の指定
ジェネリクスを使ったメソッドでは配列を指定することもできます。
実際のコードを見てみましょう。同様にnumber型の時は数値が、string型の時は文字列がきちんと出力されていますね。
function SetArray<T>(value: T): T[] {
return [value, value]
}
console.log(SetArray<number>(10));
=> [ 10, 10 ]
console.log(SetArray<string>("バナナ"));
=> [ 'バナナ', 'バナナ' ]
クラスとジェネリクス
先程まではジェネリクスをメソッドで使用してきましたが、クラスでも同様にジェネリクスを使用することが可能です。
まずは基本となるクラス宣言を行ったコードを定義しました。
class Fruits {
constructor(public fruitsname: string) {}
say(): string {
return this.fruitsname;
}
}
var favorite = new Fruits("ぶどう");
console.log(favorite.say());
=> ぶどう

上記のクラス宣言したコードをジェネリクスを使って修正してみます。すると以下のように、string型で渡した「ぶどう」という言葉が3つ出力されていますね。
class Fruits<T> {
constructor(public fruitsname: T) {}
say(): T[] {
return [this.fruitsname, this.fruitsname, this.fruitsname,]
}
}
var favorite = new Fruits<string>("ぶどう");
console.log(favorite.say());
[ 'ぶどう', 'ぶどう', 'ぶどう' ]
このようにクラスでもジェネリクスを使用することができるので覚えておきましょう。
ジェネリクスの制約
最後にジェネリクスの制約についても解説していきます。
ジェネリクスはインターフェース(Interface)を用いることで制約を付けることができます。制約を具体的に言うと、「Tの型は何でもいいけど、特定のプロパティを持っている型だけにしてね」ということになります。

では先ほどのコードに制限を付与してみましょう。
class Fruits<T> {
constructor(public fruitsname: T) {}
say(): T[] {
return [this.fruitsname, this.fruitsname, this.fruitsname,]
}
}
まずはインターフェースを使用し、string型のxとyを定義します。
interface Others {
x: string;
y: string;
}
class Fruits<T> {
constructor(public fruitsname: T) {}
say(): T[] {
return [this.fruitsname, this.fruitsname, this.fruitsname,]
}
}
先ほど定義したxとyをFruitsクラスに必ず持たせたい場合は「extends」を使用します。こうすることにより、特定のプロパティを持った型(xとy)が渡されない場合はエラーが出力されるようになります。
interface Others {
x: string;
y: string;
}
class Fruits<T extends Others> {
constructor(public fruitsname: T) {}
say(): T[] {
return [this.fruitsname, this.fruitsname, this.fruitsname,]
}
}
var favorite = new Fruits<string>("ぶどう");
console.log(favorite.say());
=> error TS2344: Type 'string' does not satisfy the constraint 'Others'.
このエラーを解消するにはstring型からOthers型に修正する必要があります。こうすることによりエラーが出力されることなく、期待する結果が表示されると思われます。
interface Others {
x: string;
y: string;
}
class Fruits<T extends Others> {
constructor(public fruitsname: T) {}
say(): T[] {
return [this.fruitsname, this.fruitsname, this.fruitsname,]
}
}
var favorite = new Fruits<Others>({x: "ぶどう", y: "バナナ"})
console.log(favorite.say());
=> [
{ x: 'ぶどう', y: 'バナナ' },
{ x: 'ぶどう', y: 'バナナ' },
{ x: 'ぶどう', y: 'バナナ' }
]
まとめ
- ジェネリクスとは、引数や返り値などの型を任意に設定できる型指定方法である。
- TはTypeの略で、慣習的にジェネリクスではTを記載する。
- 返り値が配列の型指定をする場合は「T[]」と大括弧を付与する必要がある。
- ジェネリクスはインターフェースを用いることで制約を付けることができる。
参考
Generics:
https://www.typescriptlang.org/docs/handbook/generics.html
ジェネリック型:
https://typescript-jp.gitbook.io/deep-dive/type-system/generics
【TypeScript】Generics(ジェネリックス)を理解する:
https://qiita.com/k-penguin-sato/items/9baa959e8919157afcd4
今回は、引数や返り値などの型を任意に設定できる型指定方法「ジェネリクス(Generics)」について解説しました。初めは慣れない概念かと思いますが、ジェネリクスを使いこなすことでわざわざ型定義をしなくて済んだり、重複したコードを避けることができたりと様々なメリットを享受することができます。積極的に使ってみてください。