JavaScript Tips | 基本・共通ユーティリティ:安全処理 – オプショナル取得

JavaScript JavaScript
スポンサーリンク

「オプショナル取得」とは何を楽にしたいのか

オプショナル取得は、一言でいうと
あるかもしれないし、ないかもしれない値を“落とさずに”扱うための取り方」です。

業務コードだと、こんな状況が日常茶飯事です。

user.profile.address.city
JavaScript

でも実際には、

  • user が null のときがある
  • profile が存在しないユーザーがいる
  • address が登録されていないケースがある

こういう「あるかもしれない」「ないかもしれない」値を、
毎回 if でガチガチに守るのはしんどいわけです。

そこで登場するのが「オプショナル取得」です。
「なかったら undefined でいい」「なかったらデフォルト値でいい」という前提で、
“あったら使う、なければ諦める”を、短く・安全に書くためのパターンです。


標準構文としてのオプショナルチェイニング(?.)

?. の基本的な意味

JavaScript には、すでに「オプショナル取得」を支える構文が用意されています。
それが オプショナルチェイニング演算子 ?. です。

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

これはこういう意味になります。

  • user が null / undefined なら、その時点で city は undefined
  • user.profile が null / undefined なら、その時点で city は undefined
  • user.profile.address が null / undefined なら、その時点で city は undefined
  • すべて存在していれば、最後の city の値が返る

つまり、「途中でどこかがなくても、例外を投げずに undefined を返してくれる」構文です。

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 のまま」ではなく、
「なければこのデフォルト値を使いたい」という要件になります。

そのときにセットで使うのが null 合体演算子 ?? です。

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

この一行でやっていることは、

  • user?.profile?.address?.city で「安全にオプショナル取得」
  • 結果が null / undefined なら "不明" に置き換える

という二段構えです。

もう少し例を増やしてみます。

const theme = settings?.ui?.theme ?? "light";
const pageSize = settings?.paging?.pageSize ?? 20;
const lang = settings?.lang ?? "ja";
JavaScript

ここでの重要ポイントは、

  • 「存在しないかもしれない」経路を ?. でたどる
  • 「最終的に null / undefined なら ?? でデフォルトを入れる」

という役割分担をはっきりさせることです。
これが「オプショナル取得」の基本形になります。


ユーティリティ関数としての「オプショナル取得」

構文だけでもかなり戦えますが、
「パスを配列や文字列で渡してオプショナル取得したい」というニーズもよく出てきます。

パス配列版の optionalGet

function optionalGet(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(optionalGet(user, ["profile", "address", "city"])); // "Tokyo"
console.log(optionalGet(user, ["profile", "company", "name"])); // undefined
console.log(optionalGet(null, ["profile", "address"]));         // undefined
JavaScript

やっていることは、?. をループで手書きしているイメージです。

  • 途中で null / undefined に当たったら、そこで undefined を返す
  • 最後までたどれたら、その値を返す

これも立派な「オプショナル取得」です。


オプショナル取得+デフォルト値のユーティリティ

optionalGetOr で「なければこの値」

実務では、ほぼ必ず「デフォルト値」が欲しくなります。
そこで、さきほどの optionalGet に「デフォルト付き版」を足します。

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

使い方です。

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

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

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


パス文字列版のオプショナル取得

“profile.address.city” のような文字列で指定したい場合

配列ではなく、ドット区切りの文字列で指定したいこともあります。

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

使い方です。

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

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

デフォルト付きも同じノリで書けます。

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

オプショナル取得と「型チェック」を組み合わせる

オプショナル取得で「落ちない」ようにしても、
「型が違う」問題は普通に起きます。

例えば、「数値のはずが文字列だった」「配列のはずがオブジェクトだった」などです。

そこで、「オプショナル取得 → 型チェック → デフォルト」という流れを一つのユーティリティにまとめると、
かなり実務向きになります。

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

function optionalNumberOr(obj, path, defaultValue) {
  const value = optionalGet(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 = optionalNumberOr(data, ["meta", "page"], 1);
console.log(page); // 3
JavaScript

ここでは、

  • オプショナル取得で「落ちない」
  • 数値として妥当かチェックする
  • ダメならデフォルト値

という三段構えになっています。
これができると、「外から来るデータがどれだけ怪しくても、内部では“まともな値”だけを扱う」ことができます。


実務でのオプショナル取得の使いどころ

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

外部 API のレスポンスは、仕様変更やバグで平気で形が変わります。
そこで、オプショナル取得を挟んでおくと、アプリ全体が落ちにくくなります。

const userName = user?.profile?.name ?? "名無し";
const city = user?.profile?.address?.city ?? "不明";
JavaScript

あるいは、ユーティリティ版で書くとこうです。

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

どちらも、「なかったら諦める/デフォルトにする」というオプショナル取得の発想です。

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

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

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

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


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

次のようなオブジェクトを用意して、自分で optionalGet, optionalGetOr, optionalGetByPath, optionalNumberOr を実装してみてください。

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

そして、

  • 存在するパス(user.profile.name
  • 一部欠けているパス(user.profile.company.name
  • そもそも user がないケース(null を渡す)

などを試しながら、

「どこで undefined になるか」
「どこでデフォルトが効くか」

を体で覚えていくと、オプショナル取得の感覚が一気に掴めます。

そこまで行けたら、あとはあなたのプロジェクトに合わせて、
「数値版」「真偽値版」「配列版」など、少しずつバリエーションを増やしていけば、
“外から来るどんなデータにも折れないコード”にかなり近づいていきます。

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