TypeScriptのジェネリクスで汎用的なコードを書く方法

先生

TypeScriptのジェネリクスをマスターして、どんな型にも対応できる最強のコードを書こう!🚀

TypeScriptのジェネリクスとは?

TypeScriptのジェネリクスは、関数、インターフェース、クラスを定義する際に、型をパラメータ化する機能です。これにより、様々な型に対して同じロジックを適用できる、再利用性の高いコードを書くことができます。

ジェネリクスを使用すると、型安全性を保ちながら、柔軟なコードを作成できます。コンパイル時に型チェックが行われるため、実行時のエラーを減らすことができます。

例えば、配列の要素を操作する関数を考えます。ジェネリクスを使わない場合、特定の型(例えばstring型)の配列に対してのみ動作する関数を書くことになります。しかし、ジェネリクスを使えば、number型やboolean型など、様々な型の配列に対して動作する汎用的な関数を作成できます。

ジェネリクスの基本的な使い方

ジェネリクスは、山括弧 <> を使用して型パラメータを定義します。一般的に、TUVなどの大文字のアルファベットが型パラメータとして使用されます。

以下は、ジェネリクスを使用した関数の例です。

function identity<T>(arg: T): T {
  return arg;
}
let myString: string = identity<string>("hello");  // myStringは"hello"というstring型
let myNumber: number = identity<number>(123);    // myNumberは123というnumber型
let myBoolean: boolean = identity<boolean>(true);  // myBooleanはtrueというboolean型

この例では、identity関数は型Tの引数を受け取り、同じ型の値を返します。identity<string>("hello")のように、関数を呼び出す際に型パラメータを明示的に指定することもできますが、TypeScriptは型推論により自動的に型を決定することもできます。

let myString = identity("hello"); // TypeScriptは自動的にstring型を推論
let myNumber = identity(123);       // TypeScriptは自動的にnumber型を推論
let myBoolean = identity(true);      // TypeScriptは自動的にboolean型を推論

ジェネリクスを使ったインターフェースとクラス

ジェネリクスは、インターフェースやクラスでも使用できます。これにより、インターフェースやクラスのメンバ変数の型をパラメータ化できます。

以下は、ジェネリクスを使用したインターフェースの例です。

interface GenericInterface<T> {
  value: T;
  getValue(): T;
}
class MyClass<T> implements GenericInterface<T> {
  value: T;
  constructor(value: T) {
    this.value = value;
  }
  getValue(): T {
    return this.value;
  }
}
let stringClass = new MyClass<string>("hello");
let numberClass = new MyClass<number>(123);

この例では、GenericInterfaceインターフェースは型TvalueプロパティとgetValueメソッドを持ちます。MyClassクラスは、このインターフェースを実装しており、コンストラクタでvalueプロパティを初期化し、getValueメソッドでその値を返します。

ジェネリクスの制約

ジェネリクスを使用する際に、型パラメータに制約を設けることができます。これにより、特定の型のみを受け入れるように制限できます。制約は、extendsキーワードを使用して定義します。

interface Lengthwise {
  length: number;
}
function loggingIdentity<T extends Lengthwise>(arg: T): T {
  console.log(arg.length);  // Lengthwiseインターフェースのlengthプロパティにアクセスできる
  return arg;
}
loggingIdentity({value: "test", length: 10}); // OK
// loggingIdentity(123); // エラー: number型にはlengthプロパティが存在しない

この例では、loggingIdentity関数は、Lengthwiseインターフェースを実装する型Tのみを受け入れます。Lengthwiseインターフェースは、lengthプロパティを持つオブジェクトを定義しています。したがって、この関数に渡される引数は、必ずlengthプロパティを持つ必要があります。

参考リンク

まとめ

TypeScriptのジェネリクスは、型安全性を保ちながら、再利用性の高いコードを書くための強力なツールです。関数、インターフェース、クラスでジェネリクスを使用することで、様々な型に対応できる汎用的なコードを作成できます。ジェネリクスの制約を使用することで、型パラメータに特定の要件を課すこともできます。ジェネリクスを効果的に活用して、より堅牢で保守性の高いTypeScriptコードを書きましょう。