Nullish Coalescing と Optional Chaining

はじめに

今回の記事はjavascript,typescriptどちらでも利用可能であるNull 合体演算子(Nullish Coalescing)とオプショナルチェイン(Optional Chaining)についての記事になります。以下、Typescriptのコードで理解を深めましょう。

Optional Chainingの基本

interface User {
  name: string;
  age: number;
  address?: {//←ここでの?は省略可能を表す
    town: string;
  };
}

const user1: User = {
  name: "max",
  age: 22,
  address: {
    town: "Maple Town",
  },
};
const user2: User = {
  name: "Green",
  age: 19,
};
console.log(user1?.address?.town); //Maple Town
console.log(user2?.address?.town); //undifined

ここでは最後の?と.(ドット)で繋いでいるところに注目して下さい。これがOptional Chainingです。?. 演算子を使うと、チェーン内の各参照が正しいかどうかを明示的に確認せずにアクセスしていくことができます。途中のプロパティが存在していなかったら、(エラーを出さず)user2のように式が短絡されて undefined を返してくれます。
ちなみにチェーンできるのはプロパティだけじゃなくメソッドの場合でも可能です。

Nullish Coalescingの基本

以下、先程のコードを少しいじりました。

interface User {
  name: string;
  age: number;
  address?: {
    town: string;
  };
}

const user1: User = {
  name: "max",
  age: 22,
  address: {
    town: "Maple Town",
  },
};
const user2: User = {
  name: "Green",
  age: 19,
};

const town1 = user1.address?.town ?? "Tokiwa Town";
const town2 = user2.address?.town ?? "Tokiwa Town";
console.log(town1); //Maple Town
console.log(town2); //Tokiwa Town

上記のtown1やtown2で使用している ??、これが nullish coalescing です。これは左辺がnull または undefinedのときだけ右辺(Tokiwa Town) が評価されます。

このケースではNullish Coalescing(??)を次のように書き直すことが可能です。

const town1 = user1.address?.town || "Tokiwa Town";
const town2 = user2.address?.town || "Tokiwa Town";
console.log(town1); //Maple Town
console.log(town2); //Tokiwa Town

OR 演算子(||)に書き換えました。

??と||の違いは、??は左辺がnull または undefinedのときだけ右辺(Tokiwa Town) が評価されるのに対し、||では左辺がfalsyな値(0,'',null,undefinedなど)であれば右辺が評価されるという違いがあります。つまり||では 0 や空文字もスルーされてし まうので、より明示的な nullish coalescing のほうが望ましというわけです。


Optional ChainingとNullish Coalescing
最後に.?と??を組み合わせたコードを見てこの記事を終わりとします。

const pirate = [
  {
    name: "Luffy",

    address: {
      town: "Dawn Island",
    },
  },

  {
    name: "Zoro",

    address: {},
  },

  null,
];

for (let u of pirate) {
  const user = u ?? { name: "(Somebody)" };

  const town = u?.address?.town ?? "(Somewhere)";

  console.log(`${user.name} lives in ${town}`);
}
//Luffy lives in Dawn Island
//Zoro lives in (Somewhere)
//(Somebody) lives in (Somewhere)