TypeScriptでのジェネリクスの基本的な使い方
投稿日: 2025年11月18日
TypeScriptの型の扱い方について、ジェネリクスという概念の実装があります。個人的にはちょっと複雑なロジックだし、キャッチアップ時期にはあんまり深入りしなくていいかなと敬遠していましたが、課題を進めていてこのジェネリクスの恩恵に預かれそうだったのでちょっと調べてまとめてみました。(間違ってたら教えてください💦)
ジェネリクスは、型を変数や引数のように扱う仕組みです。(ジェネリックと言ったもりします。)関数や型定義を書くときに、具体的な型を決めずに「後で指定する」ことができます。
// ❌ ジェネリクスなし:毎回新しい関数を書く必要がある
function getStringValue(): string {
return "hello";
}
function getNumberValue(): number {
return 42;
}
// ✅ ジェネリクスあり:1つの関数で全ての型に対応
function getValue<T>(): T {
// Tは型引数。後で具体的な型が入る
return someValue as T;
}基本的には関数、型、クラスそれぞれの宣言では、下記のように書きます。
// 関数
function functionName<T>(param: T): T {
return param;
}
// 型
type MyType<T> = {
value: T;
getValue: () => T;
};
// クラス
class Container<T> {
constructor(private item: T) {}
getItem(): T {
return this.item;
}
}// ❌ ジェネリクスなし
function getUsers(): User[] {
// ...
}
function getPosts(): Post[] {
// ...
}
// ✅ ジェネリクスあり:1つで全てに対応
function getItems<T>(endpoint: string): T[] {
// endpointから取得して、T[]型で返す
}
// 使い方
const users = getItems<User>("/api/users"); // User[]が返される
const posts = getItems<Post>("/api/posts"); // Post[]が返されるこちらは、今回私が直面した例です。APIレスポンスのデータ型がエンドポイントごとに異なるため、any型とするか、可能性がある全ての型を|で繋げて許容する形で実装していました。
any型だと型チェックが行われないので型安全ではないため推奨されません❌。想定される全ての型を許容する例だと他のコンポーネントで呼び出す際に複数指定されている型のうちどれで型チェックすればいいのかTSが判断できずにエラーになります。
柔軟に扱いたい型の場所に型引数Tを置き、実際に型指定するときに<>内で指定してあげることができます。
// ジェネリクスなしの場合
type ApiResponse = {
status: string;
data: any; // ❌ anyは危険(型チェックなし)
};
// ジェネリクスありの場合
type ApiResponse<T> = {
status: string;
data: T; // ✅ T は呼び出し時に決まる
};
// 使い方
type UserResponse = ApiResponse<User>; // data: User
type PostResponse = ApiResponse<Post>; // data: Post
type CategoryResponse = ApiResponse<Category[]>; // data: Category[]型安全性…Union型ではなく、正確な型を指定できる。
再利用性…1つの関数で複数の型に対応
自動補完…エディタが正確にプロパティを提案
エラー検出…間違ったプロパティアクセスは即座に検出
※Uinion型…「型Tまたは型U」のような表現ができる型。
※InterSection型…「型Tかつ型U」を意味する型。
↑もっと奥が深いので調べる余地ありですね。。