TL;DR

  • 関数式を使うと、関数全体に関数型を適用できる。
  • 関数型を使うと繰り返しが減り、型と実装を分けられ、typeof fetch のように他の関数のシグネチャにも合わせられる。

内容

関数文と関数式

関数は、関数文でも関数式でも定義できる。

function rollDice(sides: number): number { ... } // 関数文
const rollDice = function (sides: number): number { ... }; // 関数式
const rollDice = (sides: number): number => { ... }; // 関数式

関数式を使うと、関数全体に対して型宣言(関数型)を適用できる。

type DiceRollFn = (sides: number) => number;
const rollDice: DiceRollFn = (sides) => { ... };

関数型を使う利点

1 つ目の利点は、繰り返しを減らせることである(DRY)。ライブラリを提供するなら、よく使うコールバックの型宣言を提供するとよい。

2 つ目は、型と関数の実装を切り離すことで、ロジックを明確にできることである。

3 つ目は、他の関数のシグネチャに合わせられることである。たとえば、レスポンスのステータスをチェックする fetch を作るとする。シグネチャは次のように書けるが、

async function checkedFetch(input: RequestInfo, init?: RequestInit) { ... }

関数型を適用すると、より簡潔に書ける。

const checkedFetch: typeof fetch = async (input, init) => { ... };

戻り値の型だけを変えたいときは、レストパラメータと Parameters ユーティリティ型を使えばよい。ただし、この書き方は関数型を適用するより面倒である。個々の引数の型を書き出すのとどちらがよいかは、状況に応じて判断する。

async function fetchANumber(
  ...args: Parameters<typeof fetch>
): Promise<number> {
  ...
}

関数型とコールバック

Array の map や filter のようにコールバックを受け取る関数を使うと、TypeScript はコールバックの引数の型を推論する。そして、推論した型をコールバックとして渡した関数式に適用する。

キーワード