「オプショナル取得」とは何を楽にしたいのか
オプショナル取得は、一言でいうと
「あるかもしれないし、ないかもしれない値を“落とさずに”扱うための取り方」です。
業務コードだと、こんな状況が日常茶飯事です。
user.profile.address.city
JavaScriptでも実際には、
userが null のときがあるprofileが存在しないユーザーがいるaddressが登録されていないケースがある
こういう「あるかもしれない」「ないかもしれない」値を、
毎回 if でガチガチに守るのはしんどいわけです。
そこで登場するのが「オプショナル取得」です。
「なかったら undefined でいい」「なかったらデフォルト値でいい」という前提で、
“あったら使う、なければ諦める”を、短く・安全に書くためのパターンです。
標準構文としてのオプショナルチェイニング(?.)
?. の基本的な意味
JavaScript には、すでに「オプショナル取得」を支える構文が用意されています。
それが オプショナルチェイニング演算子 ?. です。
const city = user?.profile?.address?.city;
JavaScriptこれはこういう意味になります。
userが null / undefined なら、その時点でcityは undefineduser.profileが null / undefined なら、その時点でcityは undefineduser.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 になるか」
「どこでデフォルトが効くか」
を体で覚えていくと、オプショナル取得の感覚が一気に掴めます。
そこまで行けたら、あとはあなたのプロジェクトに合わせて、
「数値版」「真偽値版」「配列版」など、少しずつバリエーションを増やしていけば、
“外から来るどんなデータにも折れないコード”にかなり近づいていきます。
