ジェネリクス(Generics)は、型のパラメータ化を可能にするプログラミングの仕組みです。ジェネリクスを使用することで、一つの関数やクラスで、様々な型に対応したコードを書くことができます。これによって、コードの再利用性が高まります。
ジェネリクスのメリット
-
型安全:
ジェネリクスを使用することで、型の不整合がコンパイル時に検出されます。これにより、実行時の型エラーを防ぎます。 -
再利用性:
一つの関数やクラスを、様々な型で利用できます。これによって、同じロジックを異なる型に対して繰り返し書く必要がなくなります。
ジェネリクスの基本例
以下は、TypeScriptでジェネリクスを使用した基本的な例です。
function echo<T>(arg: T): T {
return arg;
}
let resultNumber = echo<number>(123); // 123
let resultString = echo<string>("Hello"); // "Hello"
この例では、echo
関数は、任意の型T
を受け取り、同じ型T
を返す関数です。<T>
が型パラメータで、これによってarg
の型と返り値の型がT
になります。そして、この関数を呼び出す際に、<number>
や<string>
といった具体的な型を指定することで、異なる型に対応したecho
関数を利用できます。
より具体的な例
例えば、あるオブジェクトを配列に変換するようなジェネリクス関数を考えます。
function convertToArray<T>(obj: T): Array<T> {
return [obj];
}
let numberArray = convertToArray<number>(1); // [1]
let stringArray = convertToArray<string>("hello"); // ["hello"]
ここでconvertToArray
関数は、任意の型T
のオブジェクトを受け取り、そのオブジェクトを含むT
型の配列を返します。関数を呼び出す際に、<number>
や<string>
といった具体的な型を指定しています。
ジェネリクスを使わない場合
ジェネリクスを使わない場合、関数に特定の型を指定するか、またはany型を使用することになります。特定の型を指定した場合、その型の値しか受け付けません。any型を使用した場合は、どんな型の値でも受け付けますが、型安全が失われます。
特定の型を指定する場合:
function echoNumber(arg: number): number {
return arg;
}
let resultNumber = echoNumber(123); // 123
この場合、echoNumber
関数はnumber
型の引数しか受け付けません。
any型を使用する場合:
function echoAny(arg: any): any {
return arg;
}
let resultNumber = echoAny(123); // 123
let resultString = echoAny('hello'); // 'hello'
この場合、echoAny
関数はどんな型の引数でも受け付けますが、型安全が失われ、TypeScriptの利点が活かせません。
ジェネリクスとanyの差
ジェネリクスを使うと、関数やクラスが様々な型で動作できますが、それでも型安全が保たれます。これは、ジェネリクスを使う場合、型をパラメータとして渡すので、TypeScriptの型チェッカーが型の正しさをチェックできるからです。
以下のジェネリクスの例において:
function echo<T>(arg: T): T {
return arg;
}
let resultString = echo<string>("hello");
この場合、echo
関数は<string>
ジェネリクスパラメータを通して、string
型として動作します。これにより、関数内ではarg
はstring
型として認識され、型の誤りがあればコンパイルエラーが発生します。
any型との違い:
-
型安全:
any
型を使うと、型チェッカーはその値の型をチェックしないため、型安全が失われます。一方、ジェネリクスを使うと、型チェッカーはジェネリクスパラメータとして渡された具体的な型をチェックし、型安全が保たれます。 -
自動補完とリファクタリング: ジェネリクスを使用した場合、IDEの自動補完機能が効くため、開発がスムーズになります。また、リファクタリングも安全に行うことができます。
-
コードの可読性: ジェネリクスを使用すると、どの型で動作することを意図しているのかが明示的になり、コードの可読性が向上します。一方で、
any
型を使うと、どの型が来るべきかが不明確になります。
いつ何を使うか?
- 汎用的なコードを書く必要がある場合や、複数の型で動作する必要がある場合は、ジェネリクスを使用します。
- 特定の型だけを扱う必要がある場合、またはコードのシンプリシティが求められる場合は、特定の型を指定します。
その他
ジェネリクスは、特定の型(stringやnumberなど)だけでなく、インターフェースやクラスなどの複雑な型やカスタム型に対しても使用できます。
まとめ
ジェネリクスは、型をパラメータとして扱うことで、一つのコードが様々な型に対応できるようにします。これによって、型安全を保ちながらコードの再利用性が向上します。