JavaScript Tips | 基本・共通ユーティリティ:安全処理 – 安全なプロパティ取得

JavaScript JavaScript
スポンサーリンク

なぜ「安全なプロパティ取得」が業務で必須になるのか

業務コードで一番よく見るエラーの一つがこれです。

Cannot read properties of undefined (reading 'xxx')
JavaScript

つまり、

user.profile.address.city
JavaScript

のように「ネストしたプロパティ」を読もうとして、途中のどこかが undefinednull だった、というパターンです。

API レスポンス、外部サービスの結果、オプション引数、どれも「必ず入っている」とは限りません。
それなのに「ある前提」でプロパティを読んでしまうと、実行時にアプリが落ちます。

だからこそ、「途中が undefined でも落ちないで済む“安全なプロパティ取得”」をユーティリティとして持っておくと、
業務コードの安定性が一気に上がります。


まずは標準機能:オプショナルチェイニング(?.)を理解する

JavaScript には、すでに「安全なプロパティアクセス」を助けてくれる構文があります。
それが オプショナルチェイニング演算子 ?. です。

const city = user?.profile?.address?.city;
JavaScript

これは、「usernull / undefined ならそこで止まり、cityundefined になる」という動きです。

const user1 = {
  profile: {
    address: {
      city: "Tokyo",
    },
  },
};

const user2 = {}; // profile がない

console.log(user1?.profile?.address?.city); // "Tokyo"
console.log(user2?.profile?.address?.city); // undefined(エラーにならない)
JavaScript

重要なポイントは、「途中で undefined があっても、例外にならずに undefined を返してくれる」ということです。
これだけでも、かなりの「安全なプロパティ取得」が実現できます。


「安全+デフォルト値」をセットにする

多くの場面では、「undefined ならそのまま undefined でいい」わけではなく、
undefined ならデフォルト値を使いたい」ことが多いです。

例えば、「ユーザーの都市名。なければ '不明' と表示したい」というケース。

const city = user?.profile?.address?.city ?? "不明";
JavaScript

ここでは、二つのポイントが組み合わさっています。

一つ目は、?. で「途中が undefined でも落ちない」ようにしていること。
二つ目は、??(null 合体演算子)で「最終結果が null / undefined なら '不明' にする」としていること。

この組み合わせだけで、「安全なプロパティ取得+デフォルト値補完」がかなりきれいに書けます。


ユーティリティ関数としての「安全なプロパティ取得」

構文だけでもかなり戦えますが、「パスを文字列や配列で指定したい」場面もあります。
例えば、「get(obj, 'profile.address.city') みたいに取りたい」というニーズです。

パスを配列で渡す安全取得

まずは、パスを配列で渡すシンプルなユーティリティを作ってみます。

function safeGet(obj, path) {
  let current = obj;

  for (const key of path) {
    if (current == null) {
      return undefined;
    }
    current = current[key];
  }

  return current;
}
JavaScript

使い方はこうです。

const user = {
  profile: {
    address: {
      city: "Tokyo",
    },
  },
};

console.log(safeGet(user, ["profile", "address", "city"])); // "Tokyo"
console.log(safeGet(user, ["profile", "company", "name"])); // undefined(エラーにならない)
console.log(safeGet(null, ["profile", "address"]));         // undefined
JavaScript

ここでの重要ポイントは、「途中で null / undefined を見つけたら、そこで undefined を返して終わる」というルールです。
これにより、「どこかが欠けていても絶対に例外にならない」ことが保証されます。


デフォルト値付きの safeGet

実務では、「undefined ならデフォルト値を返したい」ことがほとんどなので、
デフォルト値付きのバージョンを用意しておくと便利です。

function safeGetOr(obj, path, defaultValue) {
  const value = safeGet(obj, path);
  return value === undefined ? defaultValue : value;
}
JavaScript

使い方です。

const city = safeGetOr(user, ["profile", "address", "city"], "不明");
const company = safeGetOr(user, ["profile", "company", "name"], "不明");

console.log(city);    // "Tokyo"
console.log(company); // "不明"
JavaScript

ここでのポイントは、「undefined のときだけデフォルトを使う」という挙動にしていることです。
null をどう扱うかは要件次第ですが、「null は“意図的に空”とみなす」なら、この実装がちょうどよくなります。


パスを文字列で指定したい場合

'profile.address.city' のような文字列で指定したい」ということもよくあります。
その場合は、文字列を分割してから safeGet に渡す形にできます。

function safeGetByPath(obj, pathString, separator = ".") {
  if (!pathString) return obj;
  const path = pathString.split(separator);
  return safeGet(obj, path);
}
JavaScript

使い方です。

const city = safeGetByPath(user, "profile.address.city");
const company = safeGetByPath(user, "profile.company.name");

console.log(city);    // "Tokyo"
console.log(company); // undefined
JavaScript

さらにデフォルト値付きも簡単に作れます。

function safeGetByPathOr(obj, pathString, defaultValue, separator = ".") {
  const value = safeGetByPath(obj, pathString, separator);
  return value === undefined ? defaultValue : value;
}
JavaScript

「安全なプロパティ取得」と「型チェック」を組み合わせる

安全に取得できても、「型が想定と違う」ことはよくあります。
例えば、「数値のはずが文字列だった」「配列のはずがオブジェクトだった」などです。

そこで、「安全取得 → 型チェック → デフォルト」という流れをユーティリティでまとめると、さらに実務向きになります。

例:数値として扱えるときだけ採用する

function safeGetNumberOr(obj, path, defaultValue) {
  const value = safeGet(obj, path);

  if (typeof value === "number" && Number.isFinite(value)) {
    return value;
  }

  if (typeof value === "string") {
    const n = Number(value);
    if (Number.isFinite(n)) {
      return n;
    }
  }

  return defaultValue;
}
JavaScript

使い方です。

const data = {
  meta: {
    page: "3",
  },
};

const page = safeGetNumberOr(data, ["meta", "page"], 1);
console.log(page); // 3
JavaScript

ここでは、「安全に取得する」だけでなく、「数値として妥当かどうか」まで見ています。
こうしておくと、「変な値が紛れ込んでも、内部では常に“まともな数値”として扱える」状態を作れます。


実務での具体的な利用イメージ

API レスポンスの防御的アクセス

外部 API のレスポンスは、仕様変更やバグで平気で形が変わります。
そこで、「安全なプロパティ取得」を挟んでおくと、アプリ全体が落ちにくくなります。

const userName = safeGetOr(apiResponse, ["user", "profile", "name"], "名無し");
const city = safeGetOr(apiResponse, ["user", "profile", "address", "city"], "不明");
JavaScript

これなら、「user が null だった」「profile がなかった」という場合でも、
画面には最低限の情報を出しつつ、ログなどで異常を検知する、という設計ができます。

設定オブジェクトの読み取り

設定ファイルや環境変数から読み込んだオブジェクトも、欠けていることがよくあります。

const pageSize = safeGetNumberOr(config, ["paging", "pageSize"], 20);
const theme = safeGetOr(config, ["ui", "theme"], "light");
JavaScript

こうしておけば、「設定が一部欠けていても、アプリは動き続ける」状態を作れます。


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

次のようなオブジェクトを用意して、自分で safeGet, safeGetOr, safeGetByPath, safeGetNumberOr を実装して試してみてください。

const data = {
  user: {
    profile: {
      name: "山田",
      address: {
        city: "Tokyo",
      },
    },
  },
};
JavaScript

存在するパスと存在しないパスを混ぜて、

"user.profile.name"
"user.profile.address.city"
"user.profile.company.name"
"user.settings.theme"
JavaScript

などを安全取得してみると、「どこで落ちなくなるのか」「どこでデフォルトが効くのか」が体感できます。

ここまでできれば、「安全なプロパティ取得」はもう実務レベルです。
あとは、あなたのプロジェクトに合わせて、「数値版」「配列版」「真偽値版」などを少しずつ増やしていけば、
“外から来るどんなデータにも折れないコード”に近づいていきます。

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