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
return callback();
}
この `
- `T` は型変数であり、渡された関数の型をそのまま保持します。
- `extends (…args: any[]) => any` は、「Tは少なくとも関数であること」を強制しています。
—
3. 実践:より厳格な制約へ
もっと実用的な例として、「数値を受け取って文字列を返す関数」だけに限定したい場合を考えてみましょう。
// 数値を受け取り、文字列を返す関数型のみを許可する制約
function processNumber
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エンジニアの醍醐味ですから。
それでは、また次のコードの深淵でお会いしましょう。