オプショナルチェーン(はてなドット?.)とは?「使い方」などを解説!

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.detailsundefinedまたは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.detailsfalsy(例えば、0""など)である場合にも undefinedを返すことにに注意してください。

論理AND演算子(&&)を使用する方法

const ageOfUserB = userB.details && userB.details.age;

この方法では、userB.detailsundefinednullでない場合(truthyである場合)、userB.details.ageにアクセスします。userB.detailsundefinednullである場合(falsyである場合)、式はundefinednullを返します。

ただし、この方法ではundefinednull以外にも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

あわせて読みたい

「ドット記法」と「ブラケット記法」の違いについては下記の記事で詳しく説明しています。興味のある方は下記のリンクからぜひチェックをしてみてください。

オプショナルチェーン(?.)の用途

今まではオブジェクトのプロパティにアクセスする際にオプショナルチェーン(?.)を用いていましたが、以下の用途でもオプショナルチェーン(?.)を使用することができます。

  • 関数やメソッドを呼び出す時
  • 配列の要素にアクセスする時

各用途についてサンプルコードを用いて説明します。

関数やメソッドを呼び出す時にオプショナルチェーン(?.)を用いる

存在しない可能性のある関数やメソッドを呼び出す時にもオプショナルチェーン(?.)を使用することができます。括弧の前にオプショナルチェーン(?.)を書くのがポイントです。以下にサンプルコードを示します。

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を返す。

お読み頂きありがとうございました。