TypeScriptでジェネリック関数を定義する基本構文を徹底解説!初心者でも理解できるGenerics入門
生徒
「先生、TypeScriptの“ジェネリック関数”っていう言葉を聞いたんですが、いったい何なんですか?」
先生
「良い質問ですね。TypeScriptの“ジェネリクス(Generics)”は、いろいろな型に対応できる関数を作る仕組みなんです。」
生徒
「いろいろな型に対応できるってどういう意味ですか?」
先生
「たとえば、文字でも数値でも同じように処理したいとき、ジェネリクスを使うと、1つの関数でどちらにも対応できるようになるんです。」
生徒
「なるほど!でも、TypeScriptでどう書くんですか?」
先生
「では、ジェネリック関数の基本構文を具体的に見ていきましょう!」
1. ジェネリック関数とは?
TypeScriptのジェネリック関数(Generic Function)とは、異なる型でも共通の処理を行えるようにする関数のことです。通常の関数では、引数や戻り値の型を固定してしまいますが、ジェネリクスを使うと、呼び出し時に型を自由に指定できます。
たとえば、「文字列を受け取る関数」と「数値を受け取る関数」をそれぞれ書く代わりに、ジェネリック関数を使えば1つの関数で両方に対応できるのです。これにより、コードの再利用性(同じ処理を何度も書かなくてよい)が高まります。
2. ジェネリック関数の基本構文
ジェネリック関数は、関数名の前に山かっこ「<>」を使って型パラメータを指定します。この中に一時的な型の名前を入れます。慣例的に、T(Typeの略)がよく使われます。
基本的な構文は次のようになります。
function 関数名<T>(引数: T): T {
return 引数;
}
この構文の意味をひとつずつ見ていきましょう。
<T>:型パラメータです。ここでTという型を仮で定義します。(引数: T):引数の型をTにしています。つまり、呼び出すときに実際の型が入ります。: T:戻り値の型もTなので、受け取った型と同じ型を返すという意味です。
3. ジェネリック関数の実例を見てみよう
たとえば、渡された値をそのまま返す関数を作ってみます。ジェネリック関数を使うと、型を固定せずに定義できます。
function echoValue<T>(value: T): T {
return value;
}
// 文字列の場合
console.log(echoValue("Hello TypeScript!"));
// 数値の場合
console.log(echoValue(123));
このコードを実行すると、次のような結果になります。
Hello TypeScript!
123
このように、echoValue関数は、渡した値が文字列でも数値でも正しく動作します。これは、型パラメータTが呼び出し時に自動的に推論(判断)されるためです。
4. 型を明示的に指定する書き方
TypeScriptのジェネリクスでは、型推論に任せることもできますが、明示的に型を指定することもできます。型を指定するには、関数を呼び出すときに山かっこ「<>」を使って型名を渡します。
console.log(echoValue<string>("TypeScript勉強中"));
console.log(echoValue<number>(2025));
このように書くことで、「この関数は文字列を扱う」「この関数は数値を扱う」と明確に示すことができます。
5. 複数の型パラメータを使う
ジェネリック関数は、1つの型だけでなく、複数の型を扱うこともできます。たとえば、2つの異なる型を引数にとる関数を作る場合、次のように書きます。
function showPair<T, U>(first: T, second: U): void {
console.log(`1つ目: ${first}, 2つ目: ${second}`);
}
showPair("りんご", 100);
showPair(true, "TypeScript");
このコードでは、TとUという2つの型パラメータを定義しています。これにより、異なる型を同時に扱えるようになります。
6. ジェネリック関数を使うメリット
ジェネリック関数を使う最大のメリットは、型の安全性と柔軟性を両立できることです。具体的には次のような利点があります。
- 型安全(type safety): 間違った型を渡したときにコンパイルエラーで気づけます。
- 再利用性(reusability): 同じ処理を複数の型で共通化できます。
- 明確な型推論: TypeScriptが自動で型を判断してくれるので、コードがシンプルになります。
たとえば、配列をそのまま返す関数を作る場合も、ジェネリクスを使うと簡単に書けます。
function identityArray<T>(arr: T[]): T[] {
return arr;
}
console.log(identityArray([1, 2, 3]));
console.log(identityArray(["A", "B", "C"]));
このように、1つの関数でさまざまな型の配列を処理できるのがジェネリクスの強みです。
7. any型との違いを理解しよう
「いろいろな型を扱えるなら、any型を使えばいいのでは?」と思うかもしれません。しかし、any型は型チェックを行わないため、プログラムの安全性が下がります。
一方、ジェネリクスは「型を柔軟に扱いながらも、その型を保持」します。つまり、渡した型情報が関数内でも維持されるのです。これが大きな違いです。
// any型の例(型情報が失われる)
function anyEcho(value: any): any {
return value;
}
// Genericsの例(型情報を保持)
function genericEcho<T>(value: T): T {
return value;
}
let str = genericEcho("TypeScript");
// strはstring型として扱われる(型推論が有効)
このように、ジェネリクスを使うと、型の安全性を保ちながら柔軟なコードを書くことができます。
まとめ
TypeScriptのジェネリック関数は、さまざまな型を柔軟に扱いながら、ひとつの共通した処理を安全に保つための重要な仕組みです。この記事で取り上げた基本構文や型パラメータの考え方は、ジェネリクスを理解するうえで欠かせない基礎となります。特に、ジェネリクスの特徴である「呼び出し時に型を決められる仕組み」は、コードの再利用性を飛躍的に高め、同じ処理を複数回書く必要をなくすため、効率的なプログラム設計につながります。また、ジェネリック関数では型推論が非常に優秀で、複雑なコードでも正しい型を保ちながら扱うことができる点も利点として挙げられます。 さらに、複数の型パラメータを使った関数設計は、柔軟かつ表現力豊かなコード構築に役立ちます。異なる型を同時に扱う構造は多数のプログラムで必要とされ、そのたびにジェネリクスのメリットを活かすことで、安全性や保守性の面で大きく貢献してくれます。特に、配列や複数データの取り扱いでジェネリクスを使うと、型情報を保ったまま汎用的な処理を書くことができます。型の矛盾を避けながら、より読みやすく整ったコードを維持できる点は、学習者にとって理解を深める良いきっかけとなるでしょう。 また、any型との違いを実際のコード比較で見てきたように、型の安全性を意識した設計ではジェネリクスが大きな効果を発揮します。特定の型に依存しない柔軟さを残しながら、正しい型推論を維持できることはプログラムの堅牢性を支える重要な視点です。型情報が失われるany型と対照的に、ジェネリクスでは渡された型のまま扱えるため、関数内でも意図しない動作を防ぎやすくなります。 以下に、今回の記事内容を振り返るためのサンプルコードを追加します。ジェネリクスを活用した簡単な関数とクラスを組み合わせた例です。
サンプルコードで理解を深めよう
class Wrapper<T> {
private item: T;
constructor(item: T) {
this.item = item;
}
getItem(): T {
return this.item;
}
}
function wrapAndShow<T>(value: T): void {
const wrapped = new Wrapper<T>(value);
console.log("ラップされた値:", wrapped.getItem());
}
wrapAndShow("文字列の例");
wrapAndShow(456);
wrapAndShow(true);
この例では、ジェネリッククラスとジェネリック関数を同時に活用し、どのような型でも扱える柔軟な構造を構築しています。このようにジェネリクスは関数だけでなく、クラス、配列、データ構造にも応用でき、扱う型に統一性を持たせながら多様なロジックに対応できる強力な道具となります。扱うデータが増えるほどジェネリクスの価値は高まり、プログラム全体の見通しが良くなるため、継続して学びながらさまざまな場面で利用していくことで理解がより深まるでしょう。
生徒
「先生、ジェネリック関数って最初はむずかしいものだと思っていたけれど、型をそのまま維持しながら使えるところがとても便利だと感じました!」
先生
「その理解はとても良いですね。型が変わっても同じ関数で処理できるのは、ジェネリクスの大きな魅力です。特に配列や複数データを扱う場面では役立ちますよ。」
生徒
「any型と違って型が失われないという点も安心できますね。間違えて別の型を扱ってしまう心配が減りそうです!」
先生
「その通りです。ジェネリクスは柔軟さと安全性の両方を持っているので、さまざまなアプリケーションで活躍します。これからもいろいろなパターンを試して、理解を深めていきましょう。」
生徒
「はい!今回のコードも参考にしながら、もっと多くのケースでジェネリクスを使ってみます!」