関数型エイリアスってそもそも何?
まず一言でいうと、関数型エイリアスは「関数の“形”に名前をつける仕組み」です。
「この関数は、こういう引数を受け取って、こういう型を返す」という“関数の型”に、わかりやすいラベルをつけて再利用できるようにするものです。
type Add = (a: number, b: number) => number;
TypeScriptこれは、「number を2つ受け取って number を返す関数」を Add という名前で呼べるようにした、ということです。
以降は「(a: number, b: number) => number」と毎回書く代わりに、Add と書けば同じ意味になります。
関数が増えてきたり、あちこちで“同じ形の関数”を扱いたくなったとき、ここに効いてきます。
一番基本の形:関数の“型”に名前をつける
「関数の型」をエイリアスにする書き方
まず、関数型エイリアスの基本形をはっきりさせましょう。
type Greet = (name: string) => string;
TypeScriptこの1行には、次の意味があります。
「Greet という名前は、“name: string を引数に取り、string を返す関数型”の別名ですよ」
これを使うと、関数を書くときにこうできます。
const greet: Greet = (name) => {
return `Hello, ${name}`;
};
TypeScriptここでは greet: Greet と書くことで、
「greet には、Greet 型(= string を受け取って string を返す関数)しか代入できない」
という制約を TypeScript に伝えています。
もし約束を破ろうとすると、ちゃんと怒ってくれます。
const greet: Greet = (name) => {
return 123; // エラー: Type 'number' is not assignable to type 'string'.
};
TypeScript関数型エイリアスは、「こういう形の関数だけ入ってきてほしい」という“関数のインターフェース”を与えるものだと捉えてください。
同じ形の関数を何個も定義するときのメリット
例えば、「number を2つ受け取って number を返す関数」が何個も出てくる場合。
type BinaryNumberOp = (a: number, b: number) => number;
const add: BinaryNumberOp = (a, b) => a + b;
const sub: BinaryNumberOp = (a, b) => a - b;
const mul: BinaryNumberOp = (a, b) => a * b;
TypeScript「足し算」「引き算」「掛け算」は、それぞれ実装は違うけれど、「関数としての形」は同じです。
その“形”を BinaryNumberOp という一つの型エイリアスにしておくことで、
コードの見通しが良くなり
型定義の重複がなくなり
「この3つは同じレベルの操作なんだな」という関係性も伝わる
という状態になります。
関数型エイリアスを引数に使う(コールバックの設計)
「こういう関数を渡してね」と型で指定する
関数型エイリアスが一番よく活躍するのは、「関数を引数に取る関数」を書くときです。
例えば、文字列を加工する処理を外から差し替えたい、という場面を考えます。
type StringTransformer = (value: string) => string;
function processLine(line: string, transform: StringTransformer) {
const result = transform(line);
console.log(result);
}
TypeScriptここで StringTransformer は、「string を受け取って string を返す関数」という意味です。processLine の第2引数 transform は、その型の関数だけを受け付けます。
使う側は、こんなふうに関数を渡します。
const toUpper: StringTransformer = (value) => value.toUpperCase();
const addPrefix: StringTransformer = (value) => "[LOG] " + value;
processLine("hello", toUpper); // "HELLO"
processLine("hello", addPrefix); // "[LOG] hello"
processLine("hello", (v) => v + "!"); // 直接アロー関数でもOK
TypeScriptここで守られている約束は明確です。
transform は必ず string を受け取り
必ず string を返す
それ以外の形の関数は渡せません。
「どういう関数を渡してよくて、どういう関数はダメか」を、関数型エイリアスがはっきり線引きしてくれるイメージです。
オブジェクトの“メソッド”にも関数型エイリアスを使う
プロパティとしての関数の型をきれいに書く
オブジェクトの中に「関数のプロパティ」を持つときも、関数型エイリアスはよく使われます。
type LoggerFn = (message: string) => void;
type Logger = {
info: LoggerFn;
error: LoggerFn;
};
const logger: Logger = {
info: (msg) => {
console.log("[INFO]", msg);
},
error: (msg) => {
console.error("[ERROR]", msg);
},
};
logger.info("start");
logger.error("something went wrong");
TypeScriptここでは、
LoggerFn … 「string を受け取って何も返さない関数」Logger … その LoggerFn を info と error プロパティに持つオブジェクト
という関係になっています。
もし info の型だけ変えてしまうと、すぐに型エラーになります。
const badLogger: Logger = {
info: (msg) => msg.length, // エラー: 戻り値が void ではない
error: (msg) => console.error(msg),
};
TypeScript「このオブジェクトにぶら下がっている関数たちは、全部この形ですよ」というルールを、関数型エイリアスで一括管理できるのが気持ちいいポイントです。
関数型エイリアスを設計するときの“思考の順番”
ここからが一番大事なところです。
syntax(記法)より、「どう考えるか」が肝です。
関数型エイリアスを書く前に、必ず自分にこう問いかけてください。
この関数は、何を受け取って
何を返す関数であるべきか?
例えば「API からデータを読み込む関数」を考えます。
ID(string)を1つ受け取り
非同期に User 型のデータを返す
頭の中では、きっとそうイメージしているはずです。
それをそのまま型に落とすとこうなります。
type LoadUser = (id: string) => Promise<User>;
TypeScriptこれだけです。
この1行で、
「LoadUser は、“string を受け取って Promise<User> を返す関数”という役割だ」
と宣言できました。
あとは、それを変数や引数にくっつけていくだけです。
const loadUserFromApi: LoadUser = async (id) => {
const res = await fetch(`/api/users/${id}`);
return res.json();
};
function showUserName(loadUser: LoadUser) {
// ここでも同じ型を使い回せる
}
TypeScript「まず“どういう関数が欲しいか”を言葉にして、それを (引数の型) => 戻り値の型 に変換する」
この繰り返しが、関数型エイリアス設計の本質です。
関数型エイリアスを使うべきタイミングと、その意味
最後に、「いつ関数型エイリアスを使うのが良いか」を感覚で掴んでほしいです。
同じ形の関数が何度も出てくるとき
→ その“形”に名前をつけてあげると、読み手にも伝わるし自分も楽になります。
「この関数はこういう役割を持っている」と、名前で表したいとき
→ StringTransformer, Predicate<T>, Loader, LoggerFn など、
役割を表す名前をつけることで、コードが“仕様書っぽく”なります。
関数を引数として受け渡しする箇所が増えてきたとき
→ 関数型エイリアスを挟むことで、「渡す側」「受け取る側」が同じ契約を見て話せるようになります。
そして一番大事なのは、
「type で名前をつけた瞬間、その関数型は“プロジェクトの言葉”になる」ということです。
(value: string) => string という無機質な型が、type StringTransformer = (value: string) => string;
と名前を与えられた瞬間、「これは“文字列を変換する関数”なんだ」と意味を持ち始めます。
関数型エイリアスは、
ただの省略記法ではなく、「関数に役割と言葉を与えるための道具」だと捉えてみてください。
その感覚で使い始めると、急にコードが“自分の言葉で書かれている”ように感じられてきます。
