アロー関数でも「型の考え方」は普通の関数と同じ
まず一番大事なところから。
アロー関数でも、「引数の型」と「戻り値の型」を決める考え方は、普通の function とまったく同じです。
// 通常の関数
function add(a: number, b: number): number {
return a + b;
}
// アロー関数
const addArrow = (a: number, b: number): number => {
return a + b;
};
TypeScript違うのは書き方だけで、「何を約束しているか」は同じです。
(a: number, b: number): number => ...
この部分が「引数の型」と「戻り値の型」を表しています。
a: number, b: number… 引数はどちらもnumber): number… 戻り値はnumber
「アロー関数も、引数と戻り値の型を“かっこ”のところに書くだけ」
まずはこの感覚を持ってしまうと楽になります。
アロー関数の基本的な型の書き方
その1:変数に直接「引数型+戻り値型」を書くパターン
一番よく見る基本形です。
const greet = (name: string): string => {
return `Hello, ${name}`;
};
const message = greet("Taro"); // message: string
// greet(123); // エラー: number は string に渡せない
TypeScriptポイントは、
name: string… 引数の型): string… 戻り値の型
この2つを、=> の前に書くだけです。
その2:戻り値が明らかなときは型推論に任せる
TypeScript は戻り値型を推論してくれます。
const double = (n: number) => {
return n * 2;
};
// 戻り値型は number と推論される
TypeScript- 学習中の小さな関数 → 戻り値型を省略してもOK
- 実務での共通関数・複雑な関数 → 戻り値型を書いた方が安全
という使い分けで考えるとよいです。
アロー関数に「関数型」を先に付けてから中身を書くスタイル
その1:変数に関数型を付けてから代入する
アロー関数の型で、実務でよく使われるパターンがこれです。
type Add = (a: number, b: number) => number;
const add: Add = (a, b) => {
return a + b;
};
TypeScriptここでは、
type Add = (a: number, b: number) => number;→ 「2つの number を受け取り、number を返す関数型」const add: Add = ...;→ 「addは Add 型の関数ですよ」と宣言
という流れになっています。
この書き方のメリットは、
- 関数の「形」を別名(
Add)として再利用できる - 引数名を変えても型エラーにならない(位置と型だけ合っていればよい)
const add2: Add = (x, y) => x + y; // OK(名前は違ってもいい)
TypeScript「まず関数の“型”を定義して、それに沿うアロー関数を書く」
この考え方を持っておくと、大きなコードでも整理しやすくなります。
その2:インラインで関数型を書く
わざわざ type 名を作らず、その場で関数型を書くこともできます。
const add: (a: number, b: number) => number = (a, b) => {
return a + b;
};
TypeScript- 左側の
: (a: number, b: number) => numberが「関数の型」 - 右側が「その実装」
この書き方もよく出てくるので、「=> が2回出てきて混乱するかもしれないけど、左は型、右は実装」と覚えておくと安心です。
アロー関数をコールバックで使うときの型
引数側に型があるときは、アロー関数側は省略してよい
よくあるのは配列メソッドです。
const numbers = [1, 2, 3];
const doubled = numbers.map((n) => n * 2);
TypeScriptmap の定義側が「このコールバックは (value: number) => 何か ですよ」と型を持っているので、
アロー関数側では n の型を省略しても、TypeScript が number と推論してくれます。
つまり、
numbers.map((n: number) => n * 2); // と書いてもOKだが、冗長
TypeScriptということです。
「呼び出される側(map)が引数の型を知っているときは、渡す側(アロー関数)は型を省略できる」
これがコールバックでのよくあるパターンです。
自分でコールバックを受け取る関数を作る場合
自分で「関数を引数に取る関数」を作るときは、関数型をしっかり書きます。
type StringFn = (value: string) => void;
function process(fn: StringFn) {
fn("hello");
}
process((v) => {
console.log(v.toUpperCase()); // v は string と推論される
});
TypeScriptStringFnが「string を受け取って何も返さない関数型」processは「StringFn 型の関数を受け取る」- 渡したアロー関数は「value が string であること」を自動で保証される
このパターンを何度か書いておくと、
「関数の型を先に決める → アロー関数で実装する」流れが自然になります。
アロー関数とオブジェクト・型エイリアスの組み合わせ
オブジェクトのメソッドとしてアロー関数を使う
こんな書き方もできます。
type Counter = {
value: number;
increment: (amount: number) => void;
};
const counter: Counter = {
value: 0,
increment: (amount) => {
counter.value += amount;
},
};
counter.increment(5);
TypeScriptここでは、
type Counterに「increment は(amount: number) => void」という関数型を定義counterオブジェクトで、その型に合うアロー関数を実装
という形になっています。
アロー関数の型を設計するときは、
「まず“こういう関数が欲しい”という形を type で書く → それに沿うアロー関数を書く」
この順番で考えると、設計の筋が通りやすくなります。
アロー関数の型を意識するうえでの「軸」
最後に、アロー関数+型をどう捉えるといいか、軸だけまとめます。
アロー関数も普通の関数と同じく、「引数の型」と「戻り値の型」の約束を書く場所がある。
小さな一時的な関数 → (arg: 型): 型 => { ... } をその場で書く。
再利用したい・コールバックとして何度も使いたい → type Fn = (arg: 型) => 型; を先に作ってから、アロー関数を代入する。
配列メソッドなど「呼び出される側」が型を持っているとき → アロー関数側の引数型は省略してOK(型推論に任せてよい)。
そして一番大事なのは、
「これは、どんな引数を受け取って、どんな値を返す関数なのか?」
そのイメージを、型として素直に書き下ろすことです。
アロー関数はただの「=> 記法」ではなく、
「その関数の“形”と“役割”を、短く・はっきり書き表すための道具」だと捉えてみてください。
