🍋
【TypeScript】クロージャ・カリー化・部分適用bindの違いを整理する
以前「クロージャというよりも bind で束縛した感じの…カリー化に近いのでは?」というコメントをいただきました。
コメントをきっかけに改めてコード例と構造の違いをまとめました。
TL;DR
- どれも「関数を返す関数」で外側で一部の値を固定する技法
- 技術的にはすべてクロージャを利用している
- 設計パターン的には以下のように分類:
- クロージャ:値や設定をスコープに閉じ込める仕組み
- カリー化:n引数関数を1引数ずつ返す関数の連鎖に変換
- 部分適用:多引数関数の一部の引数を先に固定し、新しい関数を返す
背景
以前の記事で、次のようなコードを紹介しました:
function handleError(context: string) {
return (err: unknown) => {
console.error(`${context}中にエラーが発生しました:`, err);
};
}
// 使い方
await clearTask().catch(handleError("タスク削除"));
外側で context を固定し、内側で err を処理しています。
技術的にはクロージャですが、設計パターン的には「部分適用」に近い形です。
クロージャの例
クロージャは外側の変数をスコープに保持し、内側関数で参照可能にする仕組みです。
function makeLogger(prefix: string) {
return (msg: string) => {
console.log(`${prefix}: ${msg}`);
};
}
const infoLogger = makeLogger("INFO");
infoLogger("Hello"); // INFO: Hello
カリー化の例
カリー化は、n引数関数を「1引数ずつ」呼ぶ関数の連鎖に変換する技法です。
const add = (x: number) => (y: number) => x + y;
add(2)(3); // 5
const add2 = add(2);
add2(3); // 5
// こちらの書き方でも同様
function add(a: number): (b: number) => number {
return (b) => a + b;
}
add(2)(3); // 5
bind を使った部分適用の例
a = 2 を固定した新しい関数を返します。
なお、bind の戻り値の型が any のため型安全性に欠点があります。
function add(a: number, b: number) {
return a + b;
}
const addTwo = add.bind(null, 2);
addTwo(10); // 12
addTwo("ABC"); // 型チェックがされない
特徴
| 手法 | 概要 | 特徴 |
|---|---|---|
| クロージャ | 外側関数の変数をスコープに閉じ込める | 固定する値や数は自由 |
| カリー化 | n引数関数を1引数ずつ返す関数に変換 | 常に1引数ずつ渡す |
| 部分適用 | 多引数関数の一部を固定し新しい関数を返す | 固定する数は任意 |
使い分けの観点
| パターン | よく使う場面 | メリット | 注意点 |
|---|---|---|---|
| クロージャ | 設定・状態を閉じ込める | 柔軟・型安全 | 濫用でわかりにくくなる |
| カリー化 | 汎用的ユーティリティ | 柔軟に部分適用 | 書きすぎると複雑に |
| 部分適用 | 引数を先に固定したい | 明快 | bind は型安全性に欠点 |
まとめ
技術的には全部クロージャですが、設計的には「何を固定・注入するか」「何を隠すか」によって部分適用・カリー化・クロージャを使い分けます。
Discussion