【入門編】関数型を引数に取る際のジェネリクス制約(Extends)の活用 – TypeScript コア・型システムの基礎解析バイブル

TypeScriptの「型制約」を極める:関数を引数に取る際のジェネリクス制約(Extends)を掌握する

こんにちは。TypeScriptの深淵を覗き込み、日夜コンパイラと対話しているエンジニアです。

TypeScriptを触っていると、「特定の関数の型を引数として受け取りたいけれど、どんな関数でも良いわけではない」という場面に必ず遭遇しますよね。単なる `Function` 型や `any` で済ませてしまうのは、TypeScriptの恩恵を自らドブに捨てるようなものです。

今回は、ジェネリクスと `extends` キーワードを組み合わせた「型制約」という強力な武器を解説します。ここをマスターすれば、あなたのコードは格段に堅牢で、かつ柔軟なものに変わります。

—

1. なぜ「ただの関数型」では不十分なのか

まず、ダメな例を見てみましょう。

// 悪い例:anyやFunctionを使うと型安全性が崩壊する
function executeTask(callback: Function) {
// コンパイラはcallbackが何を返すか、引数に何を求めるかを知らない
return callback();
}

これでは、`executeTask` の中で何が起きるか誰にもわかりません。戻り値を利用しようとしても、すべて `any` になってしまい、実行時エラーの温床になります。

私たちが目指すべきは、「特定の型を満たす関数だけを許可し、それ以外はコンパイルエラーにする」という厳格なガードレールです。

—

2. ジェネリクスと `extends` による「型制約」

ここで登場するのが、ジェネリクス(Generics)と `extends` キーワードです。これらは「型に制限をかける」ための魔法の杖です。

構文のイメージ

`T extends U` という書き方は、「型Tは、型Uの構造を最低限持っていなければならない」という契約を結ぶことを意味します。

// 任意の関数型を受け取るための制約
function executeTask any>(callback: T) {
return callback();
}

この ` any>` という記述こそが、TypeScriptの真骨頂です。

  • `T` は型変数であり、渡された関数の型をそのまま保持します。
  • `extends (…args: any[]) => any` は、「Tは少なくとも関数であること」を強制しています。

—

3. 実践:より厳格な制約へ

もっと実用的な例として、「数値を受け取って文字列を返す関数」だけに限定したい場合を考えてみましょう。

// 数値を受け取り、文字列を返す関数型のみを許可する制約
function processNumber string>(callback: T) {
const result = callback(42);
console.log(`結果は: ${result}`);
return result;
}

// OKな呼び出し
processNumber((n: number) => `値は${n}です`);

// NGな呼び出し:コンパイルエラー!
// Argument of type ‘(s: string) => number’ is not assignable to parameter of type ‘(n: number) => string’.
processNumber((s: string) => 100);

なぜこれが素晴らしいのか?

1. 型推論の維持: `T` が関数のシグネチャを保持しているため、戻り値の型も正確に追跡できます。
2. 意図の明示: コードを読む人が「この関数は数値を受け取る必要があるんだな」と一目で理解できます。
3. 安全なリファクタリング: 後から関数の仕様を変えたくなったとき、コンパイラが壊れた箇所を即座に指摘してくれます。

—

4. 初学者が陥りやすい罠

罠:`extends` を使わずに直接型を書くこと

「制約を使わずに、直接引数の型に書いても動くのでは?」と思うかもしれません。

// 直接書く場合
function run(callback: (n: number) => string) { … }

確かに動きます。しかし、ジェネリクスを使わないと、関数呼び出しの結果が持つ特定の型情報(例えば、特定の戻り値が持つユニオン型など)を保持できない場合があります。ジェネリクスは、「渡された型をそのまま出口まで運び出す」ためのパイプラインなのです。

—

最後に:型は「ドキュメント」以上の存在

TypeScriptの型システムは、単なるバリデーターではありません。それは、あなたが書くコードが「どうあるべきか」をコンパイラと共有するための契約書です。

今回紹介した `extends` を用いた制約は、堅牢なライブラリやフレームワークを開発する際に必須の技術です。これが使いこなせるようになれば、あなたはもうTypeScript初学者ではありません。

自信を持って、複雑な型パズルを楽しんでください。型が通った瞬間の快感こそ、TypeScriptエンジニアの醍醐味ですから。

それでは、また次のコードの深淵でお会いしましょう。

タイトルとURLをコピーしました