JavaScript Tips | 基本・共通ユーティリティ:安全処理 – 存在チェック付き実行

JavaScript JavaScript
スポンサーリンク

「存在チェック付き実行」とは何を守りたいのか

存在チェック付き実行は、一言でいうと
「その値が“本当に実行していい関数か”を確認してから呼ぶ」ためのパターンです。

業務コードでよくあるのが、こういうやつです。

options.onSuccess(result);
JavaScript

でも実際には、onSuccess が渡されていないこともあります。
そのときに何も考えずに呼ぶと、

TypeError: options.onSuccess is not a function
JavaScript

でアプリが落ちます。

「あるかもしれないし、ないかもしれないコールバック」
「関数かもしれないし、別の型かもしれない値」

こういう“あいまいなもの”を安全に扱うために、
「存在チェック付き実行」というユーティリティを持っておくと、コードが一気に安定します。


まずは基本形:if で存在と型をチェックしてから実行

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

if (typeof options.onSuccess === "function") {
  options.onSuccess(result);
}
JavaScript

ここでやっていることは二つです。

一つ目は、「options.onSuccess が undefined / null ではないか」を暗黙にチェックしていることです。
typeof undefinedtypeof null"function" にはならないので、存在しなければ if の中に入りません。

二つ目は、「型が function であること」を確認していることです。
もし誰かが間違えて onSuccess: true のような値を渡しても、実行されずにスルーされます。

このパターンを毎回書くのは面倒なので、ユーティリティ関数にしてしまうのが「存在チェック付き実行」です。


ユーティリティ化した「存在チェック付き実行」

関数を受け取って、「関数なら実行する、そうでなければ何もしない」小さなユーティリティを作ってみます。

function runIfFunction(fn, ...args) {
  if (typeof fn === "function") {
    return fn(...args);
  }
  return undefined;
}
JavaScript

使い方はとてもシンプルです。

runIfFunction(options.onSuccess, result);
runIfFunction(options.onError, error);
JavaScript

ここでの重要ポイントは三つあります。

一つ目は、「呼び出し側から if 文が消える」ことです。
毎回 if (typeof ...) と書かなくてよくなり、「存在チェック付き実行」という意図が関数名に集約されます。

二つ目は、「引数を可変長で受け取って、そのまま渡している」ことです。
これにより、runIfFunction(fn, a, b, c) のように、どんな関数にも使い回せます。

三つ目は、「戻り値をそのまま返している」ことです。
関数が何か値を返す場合、それをそのまま受け取ることもできますし、
「関数がなかったときは undefined が返る」という仕様も分かりやすくなります。


オプショナルチェイニング ?.() との違いと使い分け

JavaScript には、関数呼び出しにも使えるオプショナルチェイニングがあります。

options.onSuccess?.(result);
JavaScript

これは、「onSuccess が null / undefined でなければ呼ぶ」という構文です。
ただし、ここで注意したいのは、「型チェックはしていない」という点です。

const options = {
  onSuccess: true,
};

options.onSuccess?.(); // TypeError: options.onSuccess is not a function
JavaScript

?. は「存在チェック」はしてくれますが、「それが関数かどうか」までは見てくれません。
一方、runIfFunctiontypeof fn === "function" を見ているので、
「関数以外が来たら何もしない」という安全性があります。

つまり、

構文として手軽に書きたいだけなら fn?.()
「関数であることまで保証したい」「外部から何が来るか分からない」なら runIfFunction

という使い分けができます。


デフォルト動作付きの「存在チェック付き実行」

ときどき、「関数がなかったら、代わりにこの処理をしたい」という場面があります。
例えば、「onSuccess がなければログだけ出す」などです。

その場合は、デフォルト動作付きのユーティリティを作れます。

function runIfFunctionOr(fn, fallback, ...args) {
  if (typeof fn === "function") {
    return fn(...args);
  }
  if (typeof fallback === "function") {
    return fallback(...args);
  }
  return undefined;
}
JavaScript

使い方の例です。

runIfFunctionOr(
  options.onSuccess,
  (result) => {
    console.log("成功:", result);
  },
  result
);
JavaScript

ここでは、「onSuccess があればそれを呼ぶ。なければログを出す」という振る舞いを一行で表現しています。
「存在チェック付き実行」に「フォールバック処理」を足したイメージです。


非同期関数(Promise / async)に対する存在チェック付き実行

業務では、コールバックが async 関数であることも多いです。
その場合は、戻り値が Promise になるので、await 付きで扱えるユーティリティがあると便利です。

async function runIfAsyncFunction(fn, ...args) {
  if (typeof fn === "function") {
    return await fn(...args);
  }
  return undefined;
}
JavaScript

使い方です。

await runIfAsyncFunction(options.onLoaded, data);
JavaScript

ここでも、「関数でなければ何もしない」「関数なら await して結果を返す」というシンプルなルールにしています。
「存在チェック付き実行」を非同期の世界に持ち込んだだけですが、
API フックやライフサイクルイベントなどでかなり使えます。


実務での具体的な利用シーン

コールバックオプションを持つ関数

例えば、データ取得関数に「成功時」「失敗時」のコールバックをオプションで渡せるようにしたいとします。

async function fetchUser(options = {}) {
  try {
    const res = await fetch("/api/user");
    const data = await res.json();

    runIfFunction(options.onSuccess, data);
    return data;
  } catch (error) {
    runIfFunction(options.onError, error);
    throw error;
  }
}
JavaScript

呼び出し側は、必要なコールバックだけ渡せばよくなります。

fetchUser({
  onSuccess(user) {
    console.log("ユーザー取得:", user);
  },
});
JavaScript

ここでのポイントは、「コールバックが渡されていなくても絶対に落ちない」ことです。
runIfFunction が「存在チェック付き実行」を一手に引き受けてくれているので、
fetchUser の中はとても読みやすくなります。

UI イベントハンドラのオプション実行

UI コンポーネントで、「親から渡されるかもしれないイベントハンドラ」を呼ぶ場面も典型的です。

function Button(props) {
  function handleClick(event) {
    runIfFunction(props.onClick, event);
  }

  // 仮の JSX 風
  return `<button>...</button>`;
}
JavaScript

親側は、onClick を渡してもいいし、渡さなくてもいい。
子側は、「存在チェック付き実行」で安全に呼ぶ。
このパターンは、コンポーネント設計の基本パターンの一つです。


存在チェック付き実行と「安全処理」の関係

ここまで見てきたように、「存在チェック付き実行」は、
「呼び出し先が本当に“実行可能な関数”かどうか分からない」状況での安全装置です。

安全な JSON parse
安全な JSON stringify
try-catch ラッパー
安全なプロパティ取得
オプショナル取得

と同じ系統で、「外から来るものを信用しない」という姿勢の一部です。

存在チェック付き実行をユーティリティとして持っておくと、

「このコールバック、本当にあるのかな」
「これ、関数じゃない値が来たらどうなるんだろう」

と毎回不安にならずに、
「とりあえず runIfFunction 経由で呼べば落ちない」という安心感を持って書けるようになります。


小さな練習で感覚をつかむ

次のような値を用意して、自分で runIfFunctionrunIfFunctionOr を実装して試してみてください。

const f1 = () => console.log("呼ばれた");
const f2 = "関数じゃない";
const f3 = null;
JavaScript

それぞれに対して、

runIfFunction(f1);
runIfFunction(f2);
runIfFunction(f3);

runIfFunctionOr(f1, () => console.log("フォールバック"));
runIfFunctionOr(f2, () => console.log("フォールバック"));
JavaScript

を実行してみると、「どのケースで何が起きるか」「どこで何もしないで済むか」が体感できます。

ここまで来れば、「存在チェック付き実行」はもうあなたの武器です。
あとは、実際のプロジェクトの中で「ここ、たまに undefined が来るんだよな」という場所に、
少しずつこのパターンを差し込んでいけば、コードの“壊れにくさ”がじわじわ上がっていきます。

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