【早見表】TypeScript Generics(ジェネリクス)の使用例と記法まとめ

ジェネリクスを使えば 型安全 と 再利用 を両立できます。
一度書いた処理をあらゆる型で安全に使い回せるため、開発の効率化とバグ回避を同時に叶えることができます。
この記事ではでは TypeScript ジェネリクス を中心に、早見表と具体的な活用術をまとめました。
ジェネリクス記法早見表
# | シンタックス | 機能 | ユースケース | サンプル |
---|---|---|---|---|
1 | <T> | 型パラメータ宣言 | 汎用関数 | function id<T>(v:T):T |
2 | <T, U> | 複数パラメータ | データ結合 | type Pair<T,U>=… |
3 | <T extends X> | プロパティ保証 | toString 必須 | serialize<T extends {toString():string}> |
4 | <K extends keyof O> | キー限定 | 安全なプロパティ取得 | prop<O,K extends keyof O>(o,k) |
5 | デフォルト型 =… | 後方互換 | オプション引数ラッパ | type Box<T=string>=… |
6 | 条件型 | 型分岐 | API 成否判定 | type Res<T>=T extends Error?never:T |
7 | infer | 部分抽出 | 関数引数取り出し | type Arg<F>=F extends (...a:infer A)=>any?A:never |
8 | マップ型 | プロパティ変換 | 深い readonly 化 | type RO<T>={readonly[K in keyof T]:T[K]} |
汎用関数
結論として <T>
は「あとで決まる型」を予約する仕組みです。
ジェネリック関数は一つ書くだけで数値・文字列・独自型に自由対応します。
tsfunction identity<T>(value: T): T {
return value; // T が戻り値にも伝搬
}
const num = identity(42); // T = number
const text = identity<string>('Hi'); // T を明示指定
上記のように、呼び出し側で型が決まる ためキャスト不要で補完が効きます。
データ結合
複数パラメータを使うと異種オブジェクトを安全に合体できます。
T & U
は交差型、つまり「両方のプロパティを持つ新型」です。
tsfunction merge<T, U>(a: T, b: U): T & U {
return { ...a, ...b }; // 実際の処理は単純でも型情報は厳密
}
const user = merge({ id: 1 }, { name: 'Sato' });
// user: { id: number; name: string }
型推論が交差まで導き出すので、結合後も IDE 補完が失われません。
プロパティ保証
extends
制約は「これだけは必須」という安心網です。
実行時の undefined
チェックを削れるのでロジックがスリムになります。
tsfunction toLabel<T extends { label: string }>(obj: T) {
// label が必ず存在すると TypeScript が認識
return obj.label.toUpperCase();
}
toLabel({ label: 'book', pages: 200 }); // OK
// toLabel({ pages: 10 }); // コンパイルエラー
複雑な型でも「最低限の形」を押さえるだけで汎用化が進みます。
キー限定
keyof
と組み合わせれば「存在するキーだけ」を引数にできます。
綴りミスや仕様変更によるバグをコンパイル時に防げます。
tsfunction prop<O, K extends keyof O>(obj: O, key: K): O[K] {
return obj[key];
}
prop({ x: 10, y: 20 }, 'x'); // 型安全
// prop({ x: 10 }, 'z'); // エラー: 'z' は存在しない
コードレビューで「キー間違い」を指摘するコストがゼロになります。
後方互換
デフォルト型は「パラメータ追加 → 既存コード崩壊」を防ぎます。
tstype ApiResponse<T = unknown> = {
ok: boolean;
data: T;
};
const resp: ApiResponse = { ok: true, data: { id: 1 } }; // T は unknown
過去の呼び出しに影響を与えず段階的に型を絞り込めます。
型分岐
条件型は「型で if 文を書く」イメージです。
API レイヤーで成功/失敗を厳密に分岐させられます。
tstype Result<T> = T extends Error
? { success: false; error: T }
: { success: true; value: T };
type Ok = Result<number>; // { success: true; value: number }
type Ng = Result<Error>; // { success: false; error: Error }
使う側は success
を見て型が切り替わるため、誤アクセスが起きません。
部分抽出
infer
は「型マッチングで拾った部分」を取り出すキーワードです。
tstype FirstArg<F> =
F extends (arg: infer P, ...rest: any) => any ? P : never;
type A = FirstArg<(x: number) => void>; // number
type B = FirstArg<() => void>; // never
フレームワークのヘルパーを作る際、受け取る関数のシグネチャを解析するのに重宝します。
プロパティ変換
マップ型を使うとネストしたプロパティへ一括操作できます。
tstype ReadonlyDeep<T> = {
readonly [K in keyof T]: ReadonlyDeep<T[K]>;
};
type Conf = { db: { host: string; port: number } };
type Frozen = ReadonlyDeep<Conf>; // 深い階層まで readonly
ライブラリ公開時に「設定オブジェクトは不変」を保証したい場面で活躍します。
活用例
結論を先に述べるなら、ジェネリクスは「同じ処理を複数型で安全共有」する実装全般で効果的です。
以下では React と API ラッパに落とし込み、現場イメージを膨らませます。
React 汎用リスト
tsxtype ListProps<T> = { items: T[]; render: (item: T) => React.ReactNode };
function List<T>({ items, render }: ListProps<T>) {
return <ul>{items.map(render)}</ul>;
}
ポイント
- 呼び出しごとに
T
が決まり 補完が変化 - DOM 構造は共有し デザイン修正が 1 箇所
fetch ラッパ
tsasync function fetchJson<T>(url: string): Promise<T> {
const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return (await res.json()) as T;
}
type Post = { id: number; title: string };
const post = await fetchJson<Post>('/api/post/1');
ポイント
- 戻り値の構造が確定 → UI 側で 型ガード不要
- レスポンス変更時は
Post
を更新するだけ
まとめ
ジェネリクスを活用すれば、実装量を抑えつつバグを減らし、読みやすい型付きコード実現することができます。
ぜひジェネリクス活用に役立ててください。
関連リンク
TypeScriptの記事Typescript
- article
どっちを使うべき?TypeScriptのtype(型エイリアス)とinterfaceの違いと使い分け
- article
TypeScript 5.8 で強化された型推論!その裏で潜む 落とし穴と回避策
- article
【2025年5月版 早見表】TypeScript 5.7 tsconfig.jsonの主要オプションのまとめ
- article
VSCodeで開発時のインポート補完(TypeScript)を相対パスからエイリアスにする設定
- article
【解決策】TypeScriptで発生するTS2564エラーの対応
- article
Next.js のTypeScriptプロジェクトへeslint、stylelint、prittierを導入してVSCodeで自動フォーマットするまでの手順
- article
どっちを使うべき?TypeScriptのtype(型エイリアス)とinterfaceの違いと使い分け
- article
TypeScript 5.8 で強化された型推論!その裏で潜む 落とし穴と回避策
- article
開発AIエディタ比較 Github Copilot vs Cursor vs Cline vs devin!それぞれの特徴や料金の違いを比較してみた
- article
【2025年5月版 早見表】TypeScript 5.7 tsconfig.jsonの主要オプションのまとめ
- article
【対処法】Cursorで発生する「Connection failed. If the problem persists ...」エラーの原因と対応
- article
Next.jsとEdge Runtimeを組み合わせて超爆速サーバーレス表示を実現する方法