長い関数を小さく分ける練習

入門 | 12分 で読める | 2026.06.18

公式ドキュメント

今回やること

この記事では、長い関数を小さな関数へ分けます。

関数分割の目的は、行数を減らすことではなく、読む人が処理の流れを理解しやすくすることです。

前提条件

  • TypeScriptの関数が読める
  • if文、throw、async/awaitが分かる
  • リファクタリング前後で動作を変えない意識がある

Step 1: 長い関数を見る

async function createOrder(input: OrderInput) {
 if (input.items.length === 0) {
 throw new Error("items required");
 }

 let total = 0;
 for (const item of input.items) {
 total += item.price * item.quantity;
 }

 if (total <= 0) {
 throw new Error("invalid total");
 }

 const order = {
 userId: input.userId,
 items: input.items,
 total,
 };

 await orderRepository.save(order);
 return order;
}

入力チェック、合計計算、注文作成、保存が1つに入っています。

Step 2: 処理のまとまりを見つける

コメントを付けるなら、次のように分かれます。

  • 入力チェック
  • 合計金額の計算
  • 注文オブジェクトの作成
  • 保存

コメントで説明したくなるまとまりは、関数化の候補です。

Step 3: 合計計算を切り出す

function calculateOrderTotal(items: OrderItem[]) {
 return items.reduce((total, item) => {
 return total + item.price * item.quantity;
 }, 0);
}

合計計算はDBに依存しないため、単体でテストしやすくなります。

Step 4: 入力チェックを切り出す

function validateOrderInput(input: OrderInput) {
 if (input.items.length === 0) {
 throw new Error("items required");
 }
}

入力チェックのルール変更は、この関数を見ればよくなります。

Step 5: 元の関数を流れだけにする

async function createOrder(input: OrderInput) {
 validateOrderInput(input);

 const total = calculateOrderTotal(input.items);
 if (total <= 0) {
 throw new Error("invalid total");
 }

 const order = {
 userId: input.userId,
 items: input.items,
 total,
 };

 await orderRepository.save(order);
 return order;
}

外側の関数は、処理の流れを読むための場所になりました。

よくあるエラー

状況原因対応
関数を細かくしすぎる1行ごとに切り出している意味のある単位だけ分ける
引数が多すぎる切り出す範囲が不自然データのまとまりを見直す
動作が変わったリファクタ中に仕様変更した先にテストや出力で固定する
名前が付けられない責務が混ざっているもう一段処理を整理する

まとめ

長い関数は、入力チェック、計算、外部I/Oなど、変更理由が違う単位で分けると読みやすくなります。行数だけでなく、責務とテストしやすさを見て判断します。

参考リソース

← 一覧に戻る
PR
PR
PR
PR