JavaScriptやTypeScriptのコードを見ているとはてなドット(?.
)の箇所を見かけることがあります。このはてなドット(?.
)は『オプショナルチェーン』と呼ばれています。
この記事ではJavaScriptの『オプショナルチェーン』について、
- オプショナルチェーン(
?.
)とは - オプショナルチェーン(
?.
)の特徴 - オプショナルチェーン(
?.
)の用途
などをサンプルコードを用いて分かりやすく説明するように心掛けています。ご参考になれば幸いです。
オプショナルチェーン(?.)とは
オプショナルチェーン(?.
)は?.
の左側のプロパティが存在するかをチェックする演算子です。オプショナルチェーン(?.
)の特徴を以下に示します。
?.
の左側のプロパティがundefined
またはnull
の場合?.
の右側のプロパティにアクセスせずにundefined
を返す
?.
の左側のプロパティがundefined
またはnull
でない場合?.
の右側のプロパティにアクセスする
実際にオプショナルチェーン(?.
)を用いた以下のサンプルコードを見てみましょう。サンプルコードを見れば、オプショナルチェーン(?.
)の特徴が分かると思います。
const userA = {
name: 'Taro',
details: {
age: 30,
},
};
const userB = {
name: 'Hanako',
// userBはdetailsプロパティが定義されていない
};
// 定義されているプロパティにアクセスする場合
const ageOfUserA = userA.details.age;
console.log(ageOfUserA); // ログ出力: 30
// 未定義のプロパティにアクセスする場合-オプショナルチェーン(?.)を用いない場合
const ageOfUserB = userB.details.age;
console.log(ageOfUserB);
// 以下のエラーが発生する
// Uncaught TypeError: Cannot read properties of undefined (reading 'age')
// 未定義のプロパティにアクセスする場合-オプショナルチェーン(?.)を用いた場合
const ageOfUserB = userB.details?.age;
console.log(ageOfUserB); // ログ出力: undefined
オプショナルチェーン(?.
)を用いずにuserB.details.age
のコードを実行すると、TypeError: Cannot read properties of undefined
エラーが発生します。
これは、userB
オブジェクトはuserA
オブジェクトと異なり、details
プロパティが定義されていない(undefinded
)なのに、details
プロパティにアクセスしているからです。
一方、オプショナルチェーン(?.
)を用いると、エラーが発生せず、undefinded
が返されます。なぜなら、オプショナルチェーン(?.
)を用いることで、?.
の左側にあるプロパティが定義されているかを判定してくれるからです。?.
の左側にあるプロパティが定義されていない場合には、undefinded
が返されます。
このように、プロパティの存在が保証されておらず、プロパティがundefined
またはnull
になるかもしれない場合において、オプショナルチェーン(?.
)が役立ちます。
補足
- オプショナルチェーンは英語で「Optional Chaining」と書きます。
- オプショナルチェーンはオプショナルチェイニングとも呼ばれます。
- オプショナルチェーンはES2020(ECMAScript 2020)の仕様で追加されました。
オプショナルチェーン(?.)を同様の動作を実現する方法
オプショナルチェーン ?.
を使わずに同様の動作を実現するためには、以下のような方法があります。
三項演算子を使用する
const ageOfUserB =
userB.detail === undefined || userB.detail === null
? undefined
: userB.details.age;
この方法では、まずuserB.details
がundefined
またはnull
かどうかをチェックし、そうであればundefined
を返し、そうでなければ serB.details.age
にアクセスします。
この方法はconst ageOfUserB = userB.details?.age
と同じ動作をします。
三項演算子の別の使用方法
const ageOfUserB = userB.details ? userB.details.age : undefined;
この方法も三項演算子を使用していますが、userB.details
がtruthyかどうかをチェックし、そうであればuserB.details.age
にアクセスし、そうでなければundefined
を返します。
ただし、この方法ではuserB.details
がfalsy
(例えば、0
や""
など)である場合にも undefined
を返すことにに注意してください。
論理AND演算子(&&)を使用する方法
const ageOfUserB = userB.details && userB.details.age;
この方法では、userB.details
がundefined
やnull
でない場合(truthy
である場合)、userB.details.age
にアクセスします。userB.details
がundefined
やnull
である場合(falsy
である場合)、式はundefined
やnull
を返します。
ただし、この方法ではundefined
やnull
以外にもfalsy
(例えば、0
や""
など)があるので注意してください。
オプショナルチェーン(?.)の特徴
オプショナルチェーン(?.
)の特徴を以下に示します。
- オプショナルチェーン(
?.
)はネストして使うことができる - オプショナルチェーン(
?.
)はブラケット記法にも使うことができる
各特長についてサンプルコードを用いて説明します。
オプショナルチェーン(?.)はネストして使うことができる
オプショナルチェーン(?.
)は以下のサンプルコードに示すようにネストして使うこともできます。
const userA = {
name: 'Taro',
details: {
age: 30,
address: { street: '123 Main St', city: 'Anywhere' },
},
};
const userB = {
name: 'Hanako',
// userBはdetailsプロパティが定義されていない
};
// 定義されているプロパティにアクセスする場合
const cityOfUserA = userA.details.address.city;
console.log(cityOfUserA); // ログ出力: Anywhere
// 未定義のプロパティにアクセスする場合-オプショナルチェーン(?.)をネストして用いた場合
const cityOfUserB = userB.details?.address?.city;
console.log(cityOfUserB); // ログ出力: undefined
オプショナルチェーン(?.)はブラケット記法にも使うことができる
ブラケット記法でもオプショナルチェーン(?.
)を用いることができます。括弧の前にオプショナルチェーン(?.
)を書くのがポイントです。以下にサンプルコードを示します。
const userA = {
name: 'Taro',
details: {
age: 30,
},
};
const userB = {
name: 'Hanako',
// userBはdetailsプロパティが定義されていない
};
// 定義されているプロパティにアクセスする場合
const ageOfUserA = userA['details']['age'];
console.log(ageOfUserA); // ログ出力: 30
// 未定義のプロパティにアクセスする場合-オプショナルチェーン(?.)を用いた場合
const ageOfUserB = userB['details']?.['age'];
console.log(ageOfUserB); // ログ出力: undefined
あわせて読みたい
「ドット記法」と「ブラケット記法」の違いについては下記の記事で詳しく説明しています。興味のある方は下記のリンクからぜひチェックをしてみてください。 続きを見る【JavaScript】「ドット記法」と「ブラケット記法」の違い
オプショナルチェーン(?.)の用途
今まではオブジェクトのプロパティにアクセスする際にオプショナルチェーン(?.
)を用いていましたが、以下の用途でもオプショナルチェーン(?.
)を使用することができます。
- 関数やメソッドを呼び出す時
- 配列の要素にアクセスする時
各用途についてサンプルコードを用いて説明します。
関数やメソッドを呼び出す時にオプショナルチェーン(?.)を用いる
存在しない可能性のある関数やメソッドを呼び出す時にもオプショナルチェーン(?.
)を使用することができます。括弧の前にオプショナルチェーン(?.
)を書くのがポイントです。以下にサンプルコードを示します。
const funcA = function () {
return 'hello';
};
const funcB = undefined;
// 定義されている関数を実行する場合
const resultA = funcA();
console.log(resultA); // ログ出力: hello
// 未定義の関数を実行する場合-オプショナルチェーン(?.)を用いない場合
const resultB = funcB();
console.log(resultB);
// 以下のエラーが発生する
// Uncaught TypeError: funcB is not a function
// 未定義の関数を実行する場合-オプショナルチェーン(?.)を用いた場合
const resultB = funcB?.();
console.log(resultB); // ログ出力: undefined
メソッドの場合も同様に括弧の前にオプショナルチェーン(?.
)を書きます。以下にサンプルコードを示します。
const userA = {
funcA: function () {
return 'hello';
},
};
const userB = {
str: 'hello',
// userBはメソッドが定義されていない
};
// 定義されているメソッドを実行する場合
const resultA = userA.funcA();
console.log(resultA); // ログ出力: hello
// 未定義のメソッドを実行する場合-オプショナルチェーン(?.)を用いない場合
const resultB = userB.funcB();
console.log(resultB);
// 以下のエラーが発生する
// VM91:17 Uncaught TypeError: userB.funcB is not a function
// 未定義のメソッドを実行する場合-オプショナルチェーン(?.)を用いた場合
const resultB = userB.funcB?.();
console.log(resultB); // ログ出力: undefined
配列の要素にアクセスする時にオプショナルチェーン(?.)を用いる
配列の要素を参照する際にもオプショナルチェーン(?.
)を使用することができます。括弧の前にオプショナルチェーン(?.
)を書くのがポイントです。以下にサンプルコードを示します。
const arrA = [1, 2, 3];
const arrB = undefined; // arrBは定義されていない
// 定義されている配列の要素にアクセスする場合
const resultA = arrA[0];
console.log(resultA); // ログ出力: 1
// 未定義の配列の要素にアクセスする場合-オプショナルチェーン(?.)を用いない場合
const resultB = arrB[0];
console.log(resultB);
// 以下のエラーが発生する
// Uncaught TypeError: Cannot read properties of undefined (reading '0')
// 未定義の配列の要素にアクセスする場合-オプショナルチェーン(?.)を用いた場合
const resultB = arrB?.[0];
console.log(resultB); // ログ出力: undefined
オプショナルチェーン(?.)の便利な使い方(Null合体演算子と共に使う)
Null合体演算子(??
)をオプショナルチェーン(?.
)の後に付けることで、値が存在しなかった時のデフォルト値を設定することができます。以下にサンプルコードを示します。
const userB = {
name: 'Hanako',
// userBはdetailsプロパティが定義されていない
};
// Null合体演算子(??)とオプショナルチェーン(?.)を用いてデフォルト値を設定する
const ageOfUserB = userB.details?.age ?? 0;
console.log(ageOfUserB); // ログ出力: 0
本記事のまとめ
この記事ではJavaScriptの『オプショナルチェーン』について、以下の内容を説明しました。
オプショナルチェーン(?.
)の構文について今まで説明したものをまとめると以下のようになります。
obj?.prop
obj
が存在する場合にはobj.prop
を返し、存在しない場合には、undefined
を返す。
obj?.[prop]
obj
が存在する場合にはobj[prop]
を返し、存在しない場合には、undefined
を返す。
function?.()
function
が存在する場合にはfunction
を呼び出し、存在しない場合には、undefined
を返す。
obj.method?.()
obj.method
が存在する場合にはobj.method
を呼び出し、存在しない場合には、undefined
を返す。
arr?.[index]
arr
が存在する場合にはarr[index]
を返し、存在しない場合には、undefined
を返す。
お読み頂きありがとうございました。