my.code(); Logomy.code();
TypeScript-5.ジェネリクス

my.code(); Logomy.code();

  • C++
    • Sandbox
    • 0.C++の世界へようこそ
    • 1.型システムと制御構造
    • 2.データ集合とモダンな操作
    • 3.ポインタとメモリ管理
    • 4.関数と参照渡し
    • 5.プロジェクトの分割とビルド
    • 6.クラスの基礎
    • 7.クラスを使いこなす
    • 8.継承とポリモーフィズム
    • 9.テンプレート
    • 10.STL ①:コンテナ
    • 11.STL ②:アルゴリズムとラムダ式
    • 12.RAIIとスマートポインタ
  • JavaScript
    • Sandbox
    • 0.JavaScriptへようこそ
    • 1.基本構文とデータ型
    • 2.制御構文
    • 3.関数とクロージャ
    • 4.'this'の正体
    • 5.オブジェクトとプロトタイプ
    • 6.クラス構文
    • 7.配列とイテレーション
    • 8.非同期処理①: Promise
    • 9.非同期処理②: Async/Await
  • Python
    • Sandbox
    • 0.環境構築と基本思想
    • 1.基本構文とデータ型
    • 2.リスト、タプル、辞書、セット
    • 3.制御構文と関数
    • 4.モジュールとパッケージ
    • 5.オブジェクト指向プログラミング
    • 6.ファイルの入出力とコンテキストマネージャ
    • 7.例外処理
    • 8.ジェネレータとデコレータ
  • Ruby
    • Sandbox
    • 0.rubyの世界へようこそ
    • 1.基本構文とデータ型
    • 2.制御構造とメソッド定義
    • 3.すべてがオブジェクト
    • 4.コレクション (Array, Hash, Range)
    • 5.ブロックとイテレータ
    • 6.クラスとオブジェクト
    • 7.モジュールとMix-in
    • 8.Proc, Lambda, クロージャ
    • 9.標準ライブラリの活用
    • 10.テスト文化入門
    • 11.メタプログラミング入門
  • Rust
    • Sandbox
    • 0.Rustの世界へようこそ
    • 1.基本構文と「不変性」
    • 2.関数と制御フロー
    • 3.所有権
    • 4.借用とスライス
    • 5.構造体とメソッド構文
    • 6.列挙型とパターンマッチ
    • 7.モジュールシステムとパッケージ管理
    • 8.コレクションと文字列
    • 9.エラーハンドリング
    • 10.ジェネリクスとトレイト
    • 11.ライフタイム
  • TypeScript
    • Sandbox
    • 0.TypeScriptへようこそ
    • 1.基本的な型と型推論
    • 2.オブジェクト、インターフェース、型エイリアス
    • 3.関数の型定義
    • 4.型を組み合わせる
    • 5.ジェネリクス
    • 6.クラスとアクセス修飾子
    • 7.非同期処理とユーティリティ型
my.code(); Logomy.code();

環境構築不要、その場で実践。

ut-code / my-code

Copyright © 2026 ut.code();

my.code(); について
コード実行環境についてAI質問機能についてライセンスお問い合わせ
ut.code(); について
公式ウェブサイト公式 𝕏 アカウント

第5章: ジェネリクス (Generics)

この章では、TypeScriptを使いこなす上で非常に強力な機能であるジェネリクス (Generics) について学びます。JavaやC#などの言語経験がある方には馴染み深い概念かもしれませんが、JavaScriptの世界から来た方にとっては少し抽象的に感じるかもしれません。しかし、これを理解することで、「柔軟性」と「安全性」を両立したコードが書けるようになります。

Genericsの必要性: 型を引数のように扱う

プログラミングをしていると、「処理内容は同じだが、扱うデータの型だけが違う」という場面によく遭遇します。

例えば、「引数をそのまま返す関数」を考えてみましょう。

// 数値を受け取って数値を返す
function returnNumber(arg: number): number {
  return arg;
}

// 文字列を受け取って文字列を返す
function returnString(arg: string): string {
  return arg;
}

// どんな型でも受け取れるが、戻り値の型情報が失われる(any)
function returnAny(arg: any): any {
  return arg;
}

returnNumber と returnString はロジックが完全に重複しています。一方、returnAny は重複を防げますが、TypeScriptの利点である型チェックが無効になってしまいます。

ここで登場するのが ジェネリクス です。ジェネリクスを使うと、「型そのもの」を引数のように受け取ることができます。

Generics関数

ジェネリクスを使った関数の定義を見てみましょう。 型変数は慣習として T (Typeの頭文字) がよく使われます。

ファイルを編集:identity_func.ts
// <T> は「この関数内で T という名前の型変数を使います」という宣言
function identity<T>(arg: T): T {
  console.log(`引数の型: ${typeof arg}, 値: ${arg}`);
  return arg;
}

// 使用例1: 明示的に型を指定する
const output1 = identity<string>("Hello Generics");

// 使用例2: 型推論に任せる (推奨)
// 引数が数値なので、T は number に自動的に推論される
const output2 = identity(100);

// output1は string型、output2は number型 として扱われるため安全
// output1.toFixed(2); // エラー: string型にtoFixedは存在しない
npx tsc identity_func.ts && node identity_func.js
ブラウザ上で動作するTypeScriptの実行環境です。
左上の実行ボタンを押して、このページ内のidentity_func.tsに書かれている内容を実行します。
引数の型: string, 値: Hello Generics
引数の型: number, 値: 100
出力されたファイル(編集不可):identity_func.js

このように、identity 関数は定義時点では型を固定せず、呼び出す瞬間に型が決まるという柔軟な性質を持ちます。

Genericsインターフェース

関数だけでなく、インターフェースもジェネリクスにできます。これにより、再利用性の高いデータ構造を定義できます。 例えば、「何かを入れる箱 (Box)」のような汎用的な型を作る場合に便利です。

ファイルを編集:generic_box.ts
// T型の値を持つ value プロパティがあるインターフェース
interface Box<T> {
  value: T;
}

// 文字列を入れる箱
const stringBox: Box<string> = {
  value: "TypeScript"
};

// 数値を入れる箱
const numberBox: Box<number> = {
  value: 42
};

console.log(stringBox.value.toUpperCase()); // 文字列のメソッドが使える
console.log(numberBox.value.toFixed(1));    // 数値のメソッドが使える
npx tsc generic_box.ts && node generic_box.js
ブラウザ上で動作するTypeScriptの実行環境です。
左上の実行ボタンを押して、このページ内のgeneric_box.tsに書かれている内容を実行します。
TYPESCRIPT
42.0
出力されたファイル(編集不可):generic_box.js

JavaScriptでは特に意識せずオブジェクトに様々な型の値を入れていましたが、TypeScriptではこのようにジェネリクスを使うことで、「中身が何かわからない」状態を防ぎつつ、どんな型でも許容する構造を作れます。

Genericsクラス

クラスでも同様にジェネリクスを使用できます。リストやキュー、スタックなどのデータ構造を実装する際によく使われます。

ここではシンプルな「スタック(後入れ先出し)」クラスを作ってみましょう。

ファイルを編集:simple_stack.ts
class SimpleStack<T> {
  private items: T[] = [];

  // データを追加する
  push(item: T): void {
    this.items.push(item);
  }

  // データを取り出す
  pop(): T | undefined {
    return this.items.pop();
  }

  // 現在の中身を表示(デバッグ用)
  print(): void {
    console.log(this.items);
  }
}

// 数値専用のスタック
const numberStack = new SimpleStack<number>();
numberStack.push(10);
numberStack.push(20);
// numberStack.push("30"); // エラー: number以外は入れられない
console.log("Pop:", numberStack.pop());

// 文字列専用のスタック
const stringStack = new SimpleStack<string>();
stringStack.push("A");
stringStack.push("B");
stringStack.print();
npx tsc simple_stack.ts && node simple_stack.js
ブラウザ上で動作するTypeScriptの実行環境です。
左上の実行ボタンを押して、このページ内のsimple_stack.tsに書かれている内容を実行します。
Pop: 20
[ 'A', 'B' ]
出力されたファイル(編集不可):simple_stack.js

もしジェネリクスを使わずにこれを実装しようとすると、NumberStackクラスとStringStackクラスを個別に作るか、anyを使って安全性を犠牲にするしかありません。ジェネリクスを使えば、1つのクラス定義で安全に様々な型に対応できます。

型制約 (extends): Generics型に制約を設ける

ジェネリクスは「どんな型でも受け入れられる」のが基本ですが、時には「ある特定の条件を満たす型だけを受け入れたい」という場合があります。

例えば、引数の .length プロパティにアクセスしたい場合を考えてみましょう。

ファイルを編集:without_constraints.ts
function logLength<T>(arg: T): void {
  console.log(arg.length); // エラー! Tがlengthを持っているとは限らない
}
npx tsc without_constraints.ts && node without_constraints.js
ブラウザ上で動作するTypeScriptの実行環境です。
左上の実行ボタンを押して、このページ内のwithout_constraints.tsに書かれている内容を実行します。
without_constraints.ts:2:19 - error TS2339: Property 'length' does not exist on type 'T'.

2   console.log(arg.length); // エラー! Tがlengthを持っているとは限らない
                    ~~~~~~
出力されたファイル(編集不可):without_constraints.js

すべての型が length を持っているわけではない(例: number型にはない)ため、TypeScriptはエラーを出します。 これを解決するために、extends キーワードを使って 「T は少なくともこの型を継承(適合)していなければならない」 という制約(Constraint)を設けます。

ファイルを編集:constraints.ts
// lengthプロパティを持つ型を定義
interface Lengthy {
  length: number;
}

// T は Lengthy インターフェースを満たす型でなければならない
function logLength<T extends Lengthy>(arg: T): void {
  console.log(`値: ${JSON.stringify(arg)}, 長さ: ${arg.length}`);
}

// 配列は length を持つのでOK
logLength([1, 2, 3]);

// 文字列も length を持つのでOK
logLength("Hello");

// オブジェクトも length プロパティがあればOK
logLength({ length: 10, value: "something" });

// 数値は length を持たないのでエラーになる
// logLength(100); 
npx tsc constraints.ts && node constraints.js
ブラウザ上で動作するTypeScriptの実行環境です。
左上の実行ボタンを押して、このページ内のconstraints.tsに書かれている内容を実行します。
値: [1,2,3], 長さ: 3
値: "Hello", 長さ: 5
値: {"length":10,"value":"something"}, 長さ: 10
出力されたファイル(編集不可):constraints.js

このように extends を使うことで、ジェネリクスの柔軟性を保ちつつ、関数内で安全に特定のプロパティやメソッドを利用することができます。

この章のまとめ

  • ジェネリクス (Generics) は、型を引数のように扱い、コードの再利用性と型安全性を両立させる機能です。
  • <T> のように型変数を宣言して使用します。
  • 関数、インターフェース、クラス などで利用可能です。
  • extends キーワードを使用することで、受け入れる型に制約(「最低限このプロパティを持っていること」など)を与えることができます。

ジェネリクスを理解すると、ライブラリの型定義ファイル(.d.ts)も読みやすくなり、TypeScriptでの開発力が一気に向上します。

練習問題 1: ペアを作成する関数

2つの引数を受け取り、それらを配列(タプル)にして返すジェネリクス関数 createPair を作成してください。 第1引数と第2引数は異なる型でも構いません。

要件:

  • 型引数を2つ(例: T, U)使用すること。
  • 戻り値の型は [T, U] となること。
ファイルを編集:practice6_1.ts
// ここに関数を定義してください
function createPair<T, U>(first: T, second: U): [T, U] {
    // 実装
    return [first, second];
}

// 実行例
const pair1 = createPair("score", 100);
console.log(pair1); // ["score", 100]

const pair2 = createPair(true, "valid");
console.log(pair2); // [true, "valid"]
npx tsc practice6_1.ts && node practice6_1.js
ブラウザ上で動作するTypeScriptの実行環境です。
左上の実行ボタンを押して、このページ内のpractice6_1.tsに書かれている内容を実行します。
出力されたファイル(編集不可):practice6_1.js

練習問題 2: 制約付きジェネリクス

id プロパティ(型は number または string)を持つオブジェクトのみを受け取り、その id を表示する関数 showId を作成してください。

要件:

  • extends を使用して型パラメータに制約をかけること。
  • id プロパティを持たないオブジェクトを渡すとコンパイルエラーになること。
ファイルを編集:practice6_2.ts
// 制約用のインターフェース
interface HasId {
    id: number | string;
}

// ここに関数を定義してください
function showId<T extends HasId>(item: T): void {
    console.log(`ID is: ${item.id}`);
}

// 実行例
showId({ id: 1, name: "UserA" });        // OK
showId({ id: "abc-123", active: true }); // OK

// 以下のコードはエラーになるはずです
// showId({ name: "NoIdUser" });
npx tsc practice6_2.ts && node practice6_2.js
ブラウザ上で動作するTypeScriptの実行環境です。
左上の実行ボタンを押して、このページ内のpractice6_2.tsに書かれている内容を実行します。
出力されたファイル(編集不可):practice6_2.js
前のページ« 型を組み合わせる
次のページクラスとアクセス修飾子»