0Pricing
Frontend Academy · レッスン

ジェネリクス:T extends と制約

ジェネリック関数とインターフェースを書き、extends で型パラメーターを制約し、柔軟な API のためにジェネリックのデフォルトを使います。

「ジェネリクス:T extends と制約」はCoddyKit上の無料Frontend Academyレッスンです。 これはレッスン1/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはFrontend Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Frontend Academyコースには全4レッスンが含まれています。

ジェネリクスとは

ジェネリクスを使うと、型安全性を保ちながら、複数の型で動作する関数、クラス、インターフェースを記述できます。型ごとに別々の関数を書く代わりに、型変数でパラメーター化した1つのジェネリック関数を書きます。

単純なジェネリック関数

型パラメーター T は山かっこ内で宣言します。TypeScript は引数の型から T を推論するため、呼び出し側で明示的なアノテーションを付ける必要はありません。

function identity<T>(value: T): T {
  return value;
}

identity(42);      // T is inferred as number, returns number
identity('hello'); // T inferred as string
identity<boolean>(true); // explicit

ジェネリックインターフェースと型

ジェネリクスでインターフェースや型をパラメーター化すると、再利用可能なデータ構造を表現できます。

interface Box<T> {
  value: T;
  label?: string;
}

const numBox: Box<number> = { value: 42 };
const strBox: Box<string> = { value: 'hello', label: 'greeting' };

extends — ジェネリック型の制約

T extends SomeType を使うと、T に指定できる型を制限できます。この関数は、制約を満たす型だけを受け取ります。

function getLength<T extends { length: number }>(item: T): number {
  return item.length; // safe: T is guaranteed to have length
}

getLength('hello');       // 5
getLength([1, 2, 3]);    // 3
getLength({ length: 7 }); // 7
// getLength(42);         // Error: number has no length

keyof とジェネリック制約

keyof T は、T のプロパティキーのユニオン型を生成します。ジェネリクスと組み合わせることで、型安全なプロパティアクセスが可能になります。

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
  return obj[key];
}

const user = { name: 'Alice', age: 30 };
getProperty(user, 'name');  // string
getProperty(user, 'age');   // number
// getProperty(user, 'foo'); // Error

ジェネリックのデフォルト

T = DefaultType を使うと、ジェネリックパラメーターにデフォルトの型を指定できます。適切なデフォルト値がある一方で、呼び出し側から上書きできる場合に使用します。

interface Paginated<T = unknown> {
  items: T[];
  total: number;
  page: number;
}

const rawPage: Paginated = { items: [], total: 0, page: 1 };
const typedPage: Paginated<User> = { items: [], total: 0, page: 1 };

複数の型パラメーター

関数には、互いに独立した複数の型パラメーターを指定できます。

function zip<A, B>(arrA: A[], arrB: B[]): Array<[A, B]> {
  return arrA.map((a, i) => [a, arrB[i]]);
}

zip([1, 2, 3], ['a', 'b', 'c']);
// [[1,'a'], [2,'b'], [3,'c']]

ジェネリッククラス

クラスもジェネリックにできます。スタック、キュー、observable store などのデータ構造でよく使われます。

class Stack<T> {
  private items: T[] = [];
  push(item: T) { this.items.push(item); }
  pop(): T | undefined { return this.items.pop(); }
  peek(): T | undefined { return this.items.at(-1); }
  get size() { return this.items.length; }
}

const stack = new Stack<number>();
stack.push(1);
stack.push(2);
stack.pop(); // 2

非同期ジェネリック関数

ジェネリックな非同期関数は、型付けされた API 呼び出しでよく使われます。

async function fetchJson<T>(url: string): Promise<T> {
  const res = await fetch(url);
  if (!res.ok) throw new Error(`HTTP ${res.status}`);
  return res.json() as Promise<T>;
}

const users = await fetchJson<User[]>('/api/users');
const profile = await fetchJson<UserProfile>('/api/me');

条件型での infer

条件型内の infer キーワードは、パターンから型を抽出します。ライブラリコードで、関数の戻り値の型や配列の要素型を抽出するために使用します。

type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

type ElementType<T> = T extends Array<infer E> ? E : never;

type Fn = () => { name: string };
type Result = ReturnType<Fn>; // { name: string }

ジェネリクスを使う場面

次のような場合にジェネリクスを使用します:1) 同じロジックを複数の型に適用する場合、2) 入力型と出力型の関係が必要な場合、3) 再利用可能なデータ構造やユーティリティを作成する場合です。必要以上にジェネリクスを使わないでください。ユースケースが1つだけなら、具体的な型のほうが明確です。

確認問題

T extends { length: number } という制約は何を保証しますか?

まとめ:ジェネリクス

ジェネリクスは型をパラメーター化し、1つの関数、クラス、インターフェースを複数の型で使用できるようにします。T extends SomeType は制約を追加します。keyof T はプロパティキーのユニオン型を生成します。T = Default のようなジェネリックのデフォルトを指定できます。infer は条件型から型を抽出します。再利用可能な型付きユーティリティやデータ構造にはジェネリクスを使用します。

よくある質問

「ジェネリクス:T extends と制約」レッスンは無料ですか?

はい。「ジェネリクス:T extends と制約」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Frontend Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Frontend Academyコースには全4レッスンが含まれています。

「ジェネリクス:T extends と制約」で何を学びますか?

ジェネリック関数とインターフェースを書き、extends で型パラメーターを制約し、柔軟な API のためにジェネリックのデフォルトを使います。 ブラウザで直接実行するハンズオンコードでFrontend Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Frontend Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのFrontend Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン1/4です。

「ジェネリクス:T extends と制約」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このFrontend Academyレッスンでコードを書いて実行できますか?

はい。すべてのFrontend Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. ジェネリクス:T extends と制約
  2. ユーティリティ型:Partial・Required・Pick・Omit
  3. Mapped Types と Conditional Types
  4. 型の絞り込み:typeof・instanceof・判別可能なユニオン
← Frontend Academyに戻る