TypeScript

【TypeScript】ジェネリクス(Generics)の概要とその使い方を初心者向けにまとめてみた

TypeScript

 

今回は、引数や返り値などの型を任意に設定できる型指定方法「ジェネリクス(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;
}
TはTypeの略で、ジェネリクスでは慣習的にTを記載します。

 

では上記のメソッドを呼び出してみましょう。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>("バナナ"));
=> [ 'バナナ', 'バナナ' ]
返り値が配列の型指定をする場合は、上記のように「T[]」と大括弧を付与する必要があります。

クラスとジェネリクス

先程まではジェネリクスをメソッドで使用してきましたが、クラスでも同様にジェネリクスを使用することが可能です。

まずは基本となるクラス宣言を行ったコードを定義しました。

class Fruits {
  constructor(public fruitsname: string) {}

  say(): string {
    return this.fruitsname;
  }
}

var favorite = new Fruits("ぶどう");
console.log(favorite.say());
=> ぶどう
TypeScript
【TypeScript】基本となるTypeScriptのクラス宣言について解説TypeScriptのクラス宣言について簡単に解説しています。型を定義するということ以外、基本的にはJavaScriptと変わりませんが改めてみていきましょう。...

 

上記のクラス宣言したコードをジェネリクスを使って修正してみます。すると以下のように、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の型は何でもいいけど、特定のプロパティを持っている型だけにしてね」ということになります。

TypeScript
【TypeScript】オブジェクトの型付けを行うインターフェースについて初心者向けに解説オブジェクトの型付けを行うインターフェース(Interface)について解説しています。インターフェースを使うことで、よりスッキリとした可読性の高いコードを書くことができるので、ぜひこの機会にマスターしましょう。...

 

では先ほどのコードに制限を付与してみましょう。

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'.
extendsはインターフェースの継承の時にも出てきたね。

 

このエラーを解消するには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)」について解説しました。初めは慣れない概念かと思いますが、ジェネリクスを使いこなすことでわざわざ型定義をしなくて済んだり、重複したコードを避けることができたりと様々なメリットを享受することができます。積極的に使ってみてください。