TypeScript | 基礎文法:関数の基礎 – 関数式の型

TypeScript
スポンサーリンク

「関数式の型」とは何か

まず言葉の整理からいきます。
TypeScript で「関数式の型」といったとき、だいたい次のようなものを指します。

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

この function (a: number, b: number): number { ... } の部分は「関数式」です。
これを変数 add に代入しています。

つまり、

  • 関数「定義」:function add(a: number, b: number) { ... }
  • 関数「式」 :const add = function (a: number, b: number) { ... }

という違いです。

そして「関数式の型」とは、
「この変数には、こういう引数と戻り値を持つ“関数”が入りますよ」という型のことです。

「数じゃなくて“関数そのもの”を変数に入れている」
そのときに、その「関数の形」に型をつける——これが関数式の型の話です。


一番基本:関数式に型を書く2つの場所

パターン1: 関数式の中に引数型・戻り値型を書く

いちばん素直な書き方はこれです。

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

const result = add(1, 2);  // result: number
// add("1", 2);            // エラー
TypeScript

ここでは、

  • a: number, b: number が「引数の型」
  • ): number が「戻り値の型」

という意味を持っています。

関数宣言とまったく同じ考え方ですが、
「名前付き関数」ではなく「無名関数(function の後に名前がない)」を変数に入れている形ですね。

関数式側にだけ型を書くスタイルなので、
変数 add には TypeScript が「(a: number, b: number) => number 型」と自動でつけてくれます。

パターン2: 変数側に「関数型」を書いてから、関数式を代入する

実務でよく使われるのはこちらのパターンです。

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

左側の : (a: number, b: number) => number が「関数の型」を表しています。
右側の function (a, b) { ... } は、その型に合う「実装」です。

ポイントは、

  • 変数の型として「関数の形」を宣言する
  • その宣言に合わない関数を代入しようとするとエラーになる

ということです。

たとえば、戻り値を返し忘れるとこうなります。

const add: (a: number, b: number) => number = function (a, b) {
  // return a + b; を書き忘れた
};
// エラー: Type 'void' is not assignable to type 'number'.
TypeScript

「左で“こういう関数が欲しい”と宣言し、右に“その仕様を満たす関数”を書く」
このスタイルが「関数式の型」を活かした書き方です。


関数型を type で名前を付けてから使う

関数の形に「ラベル」をつける

同じ形の関数が何回も出てくるなら、その「関数の型」に名前をつけておくとすごく楽になります。

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

const add1: Add = function (a, b) {
  return a + b;
};

const add2: Add = (a, b) => a + b;
TypeScript

ここでやっていることは、

  • type Add = (a: number, b: number) => number; → 「2つの number を受け取って number を返す関数型」に Add という名前をつける
  • const add1: Add = ...;
  • const add2: Add = ...; → 「どちらも Add 型の関数である」と宣言

です。

これの何がいいかというと、

同じ関数型を別の場所でも再利用できる
引数名が違っても、型(位置と型)が合っていれば OK

という点です。

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

「名前は a, b じゃなきゃダメ」ということはなく、
「順番と型が一致していれば同じ関数型」として扱われます。


関数式の型を「引数」として使う(コールバック)

関数を受け取る関数

関数式の型が真価を発揮するのは、「関数を引数に取る」場面です。

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

function processAndPrint(processor: StringProcessor) {
  const input = "hello";
  const output = processor(input);
  console.log(output);
}
TypeScript

ここで StringProcessor は、「string を受け取って string を返す関数型」です。

使う側は、関数式(やアロー関数)を渡します。

const toUpper: StringProcessor = function (value) {
  return value.toUpperCase();
};

processAndPrint(toUpper);       // "HELLO"
processAndPrint((v) => v + "!"); // "hello!"
TypeScript

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

  • processAndPrint に渡せるのは、「string を受け取り string を返す関数」だけ
  • 他の型の引数を取る関数や、戻り値が違う関数を渡そうとするとエラー

ということです。

「関数を“値”としてやりとりするとき、その関数の“形”にも型を付ける」
これが、関数式の型を理解するうえで一番大事なポイントです。


関数式の型推論と「どこまで書くか」のバランス

TypeScript が推論してくれる場合

たとえば、配列の map を使うとき。

const numbers = [1, 2, 3];

const doubled = numbers.map(function (n) {
  return n * 2;
});
TypeScript

ここで n の型は、numbersnumber[] であることから自動で number と推論されます。
function (n: number) とわざわざ書かなくても、型安全です。

「呼び出し元(map 側)が関数型を知っている」場合は、関数式側の型は省略してよい、というのが実務での感覚です。

自分で変数に入れるときはどうするか

一方で、自分で変数に関数を入れるときは、次のように考えます。

すぐ下でしか使わない小さな関数 → 関数式側にだけ型を書く or 推論に任せる
他の場所からも使われる関数 → 変数側に「関数型」を明示しておく

たとえば、サービス関数のように「いろんなところから呼ばれる関数」は、こうしたくなります。

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

const fetchUser: FetchUser = async function (id) {
  // ...
};
TypeScript

「この変数は、こういう関数であるべき」という約束を型で固定する
この意識を持てるようになると、関数式の型が一気に“設計の道具”になります。


「関数の型」をどう言語化するかが上達の鍵

関数式の型を考えるとき、いつも同じ問いに戻ってきます。

この関数は、どんな引数を受け取るべきか?
それぞれの引数は、どんな型をしているべきか?
この関数を呼んだとき、呼び出し側はどんな型の値を受け取れるべきか?

その答えを、
(arg1: 型1, arg2: 型2) => 戻り値の型
という形で素直に書き下す。

それを変数にくっつければ、それがそのまま「関数式の型」になります。

type Logger = (message: string, level?: "info" | "warn" | "error") => void;

const log: Logger = function (message, level = "info") {
  console.log(`[${level}] ${message}`);
};
TypeScript

この例だと、

  • message は string
  • level は省略可能で、指定するなら “info” | “warn” | “error”
  • 戻り値は使わない(void)

という「関数の設計」が、そのまま型に刻まれています。

「関数の“意図”を、型として言葉にする」
その感覚がついてくると、「関数式の型」は一気に楽しくなります。

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