TypeScript | 基礎文法:関数の基礎 – 関数を変数に代入する型

TypeScript
スポンサーリンク

「関数を変数に代入する」ときに型で何を約束しているか

まず前提をそろえます。
TypeScript では、「関数そのもの」も値として扱えて、変数に代入できます。

function add(a: number, b: number): number {
  return a + b;
}

const fn = add; // 関数をそのまま代入している
TypeScript

fn は「関数型の値」です。
ここで言いたいことはただ一つ。

「この変数には“こういう形の関数”が入りますよ」というのを、型で表現できる。

この「こういう形」が、
(引数の型たち) => 戻り値の型 という 関数型 です。
それを変数にくっつけるのが「関数を変数に代入する型」です。


基本1:変数に直接「関数型」を書くパターン

具体例で形をつかむ

一番シンプルな書き方から見ます。

const add: (a: number, b: number) => number = function (a, b) {
  return a + b;
};
TypeScript

ここで注目してほしいのは左側です。

const add: (a: number, b: number) => number
TypeScript

この部分が「add という変数には、(a: number, b: number) => number 型の関数が入る」と宣言している箇所です。

分解すると、

  • a: number, b: number
    → この関数は number を2つ引数に取る
  • => number
    → 戻り値は number

という約束になっています。

右側の function (a, b) { ... } は、その約束を守る「中身」です。
もし約束を破ると、TypeScript がちゃんと怒ります。

const add: (a: number, b: number) => number = function (a, b) {
  return "hello"; // エラー: Type 'string' is not assignable to type 'number'.
};
TypeScript

ここで守られているのは、

  • 引数の数・型が合っていない関数は代入できない
  • 戻り値の型が違う関数も代入できない

という「関数の設計」です。
「変数に関数を入れる」とき、その変数の型が“関数の仕様書”になっている と捉えてください。


基本2:アロー関数を代入するときの型

アロー関数版も考え方は同じ

同じことをアロー関数で書くとこうなります。

const add: (a: number, b: number) => number = (a, b) => {
  return a + b;
};
TypeScript

やっていることはまったく同じです。

左側:変数 add は「number, number を受け取って number を返す関数型」
右側:その仕様どおりに作られたアロー関数

なので、引数の型や戻り値を変えようとすると、やはりエラーになります。

const add: (a: number, b: number) => number = (a, b) => {
  return `${a + b}`; // エラー
};
TypeScript

「アロー関数だろうが function だろうが、変数につける型の書き方は同じ」、これだけ押さえておけばOKです。


パターン3:関数型に type 名をつけてから変数に使う

関数の「形」に名前をつける

何度も同じ形の関数を扱うなら、その「形」に名前を付けると分かりやすくなります。

type AddFn = (a: number, b: number) => number;

const add: AddFn = (a, b) => a + b;

const plus: AddFn = function (x, y) {
  return x + y;
};
TypeScript

ここでやっているのは、

  • type AddFn = (a: number, b: number) => number;
    → 関数型そのものに AddFn というラベルをつけた
  • const add: AddFn = ...; / const plus: AddFn = ...;
    → どちらも「AddFn 型の関数」を入れる変数

という構造です。

このスタイルの良さは、

  • 同じ型をあちこちで再利用できる
  • 引数の名前は違ってもOK(位置と型が合っていれば同じ関数型)

という点です。

const sum: AddFn = (x, y) => x + y; // a, b じゃなくてもOK
TypeScript

「まず type で“こういう関数が欲しい”と定義し、それを変数にくっつける」
実務での定番パターンなので、早めに慣れておくとかなり楽になります。


パターン4:関数を受け取る変数(コールバック型)にも同じ考え方を使う

例:文字列を加工する関数を受け取る変数

「関数を変数に代入する型」は、そのまま「コールバック関数の型」にもなります。

type StringFn = (value: string) => string;

const toUpper: StringFn = (v) => v.toUpperCase();
const withMark: StringFn = (v) => v + "!";

function run(fn: StringFn) {
  console.log(fn("hello"));
}

run(toUpper);   // "HELLO"
run(withMark);  // "hello!"
TypeScript

ここでは、

  • StringFn が「string を受け取って string を返す関数型」
  • toUpper / withMark は「その型の関数を代入した変数」
  • run の引数 fn: StringFn にも使っている

という状態になっています。

「関数型を一度定義しておけば、“関数を入れる変数”にも、“関数を受け取る引数”にも、同じ型を貼れる」
これがかなり強いです。


パターン5:オブジェクトのプロパティとして関数を持つ場合の型

メソッドの形も「関数を変数に代入する型」と同じ

オブジェクトの中で関数をプロパティとして持つときも、実態は「プロパティに関数を代入している変数」と同じです。

type Logger = {
  log: (message: string) => void;
};

const consoleLogger: Logger = {
  log: (message) => {
    console.log("[LOG]", message);
  },
};

consoleLogger.log("hello");
TypeScript

log: (message: string) => void;
この部分が、「log というプロパティには、こういう形の関数が入ります」という宣言です。

実際に代入するのはアロー関数だったり function 式だったりしますが、型の考え方は変わりません。


「関数を変数に代入する型」を設計するときの考え方

ここが一番大事な部分です。
記法を覚えるより、「どう考えればいいか」を身体に入れてほしい。

関数を変数に入れるとき、必ず自分に問いかけます。

この変数に入ってほしい関数は、どんな引数を取るべき?
それぞれの引数は、どんな型?
その関数は、何を返すべき?(何も返さないなら void

例えば「ユーザーをIDから読み込む関数」を変数にしたいなら、頭の中にはこういうイメージがあるはずです。

ID(string)を1つ受け取る
非同期にユーザー(User 型)を返す(Promise<User>

それをそのまま型にすると、こうなります。

type LoadUser = (id: string) => Promise<User>;

const loadUserFromApi: LoadUser = async (id) => {
  const res = await fetch(`/api/users/${id}`);
  return res.json();
};
TypeScript

「頭の中の“こういう関数が欲しい”というイメージを、そのまま (引数) => 戻り値 という形に言語化する」
これができるようになると、関数を変数に代入するときの型付けはほぼ制覇です。


まとめ:関数を変数に代入する型は「関数用の型エイリアス」

最後に、感覚だけ整理します。

関数も値なので、変数に代入できる。
その変数に「この形の関数しか入れてほしくない」と言いたいとき、
(arg1: 型1, arg2: 型2) => 戻り値の型 という関数型を付ける。
よく使う形は type FnName = (args) => return; と型エイリアスにして、変数・引数・プロパティに再利用する。

そして一番大事なのは、型を書く前にいつも自分に問いかけることです。

「この変数に入る“関数”は、本当はどんな役割を持っているんだっけ?」

その答えを、関数型として書き下ろしたものが、
「関数を変数に代入する型」です。

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