ジェネリクスは、型をパラメータ化することで、再利用性と型安全性を高めたコードを書くための強力な機能です。ジェネリクスを使うことで、様々な型に対応する柔軟な関数やクラスを作成することができます。このセクションでは、ジェネリクスの基本から関数やクラスでの利用、ジェネリック制約について説明します。
ジェネリクスの基本
ジェネリクスは、型をパラメータとして扱うことで、様々な型に対応する柔軟なコードを記述するために使用します。例えば、配列の要素の型を固定せずに扱いたい場合にジェネリクスが役立ちます。
typescript
function identity<T>(arg: T): T {
return arg;
}
let output1 = identity<string>("Hello, World!"); // T の型は string
let output2 = identity<number>(42); // T の型は number
この例では、identity
関数は型パラメータT
を受け取り、その型に応じた引数を扱うことができます。呼び出し時に具体的な型を指定することで、どの型でも対応可能な汎用的な関数が作れます。
関数とクラスでのジェネリクスの利用
ジェネリクスは関数だけでなく、クラスやインターフェースでも使用できます。これにより、様々なデータ型を扱うことができる汎用的なクラスを作成することが可能です。
関数でのジェネリクス
以下の例では、ジェネリクスを使った関数getArray
を定義しています。この関数は配列を受け取り、同じ型の配列を返します。
typescript
function getArray<T>(items: T[]): T[] {
return new Array().concat(items);
}
let numberArray = getArray<number>([1, 2, 3]);
let stringArray = getArray<string>(["a", "b", "c"]);
クラスでのジェネリクス
クラスにもジェネリクスを使うことで、様々な型に対応するデータ構造を作成することができます。例えば、スタックを実装するクラスを以下のように定義できます。
typescript
class GenericStack<T> {
private stack: T[] = [];
push(item: T): void {
this.stack.push(item);
}
pop(): T | undefined {
return this.stack.pop();
}
peek(): T | undefined {
return this.stack[this.stack.length - 1];
}
}
let numberStack = new GenericStack<number>();
numberStack.push(10);
numberStack.push(20);
console.log(numberStack.pop()); // 20
let stringStack = new GenericStack<string>();
stringStack.push("Hello");
stringStack.push("World");
console.log(stringStack.pop()); // World
上記の例では、GenericStack
クラスがジェネリック型T
を使用しており、任意の型の要素を扱うことができます。これにより、number
型やstring
型のスタックを簡単に作成できます。
ジェネリック制約
ジェネリクスを使う際には、型に制約を設けて、ジェネリック型が持つべき特定のプロパティやメソッドを定義することができます。ジェネリック制約を使うことで、扱う型に対する条件を指定することが可能です。
制約を使ったジェネリクス
例えば、オブジェクトにlength
プロパティがあることを前提とする関数を作りたい場合、ジェネリック制約を使って型に条件を課すことができます。
typescript
interface Lengthwise {
length: number;
}
function logLength<T extends Lengthwise>(arg: T): void {
console.log(arg.length);
}
logLength("Hello"); // OK, string 型には length プロパティがある
logLength([1, 2, 3]); // OK, 配列も length プロパティを持つ
// logLength(42); // エラー: number 型は length プロパティを持たない
この例では、インターフェースLengthwise
を使って、length
プロパティを持つ型であることをT
に要求しています。その結果、length
プロパティがない型は渡せなくなります。
ジェネリック制約を使うことで、型パラメータに対して条件を追加し、より強力で安全なコードを書くことができます。
まとめ
ジェネリクスを使うことで、型を柔軟に扱いながらも型安全性を保つことができます。関数やクラスにジェネリクスを導入することで、様々な型に対応できる汎用的なコードを作成でき、ジェネリック制約を使うことで、特定の条件を満たす型のみに対応することが可能になります。これにより、再利用性の高い、保守しやすいコードを書くことができるでしょう。