頑張らないために頑張る

ゆるく頑張ります

オプショナルチェーン演算子の基本

Posted at — Feb 23, 2023

概要

オプショナルチェーン演算子ってのは、JavaScript(以下、JS)にありがちな「一見すると何なのかよくわからない」かつ「ググラビリティが低い」系の記述方法です。個人的に見た目が似たようなタイプだと思っている三項演算子とかだと、まだなんとなーく解読できそうな見た目だったりしますが、この演算子は初見だと何やってるのかいまいちわかりにくい演算子です。

なにせ、hoge?.fooみたいな見た目なので・・・もう少しこう何というか、手心というか_(:3」∠)_

ただ、オブジェクトにプロパティをいろいろ付与しがちなJSだと、この演算子の使いでが結構あるので覚えておいて損はないです。というか、得しかないかも。

そんなコイツは一体なんなのかと言うと、undefinednullのプロパティに誤アクセスした際、発生するエラーを回避しながら可動性を確保できる書き方です。つまり、JSでありがちな「存在しないプロパティに誤アクセスしちゃってエラーが起きる」問題を回避できるわけです。

具体的な使い方

プロパティに対して使う基本的な使い方

オプショナルチェーン演算子は、前述の通りhoge?.fooのように書きます。これで何をしているのかというとhogeオブジェクトのfooプロパティにアクセスしています。「ん?単純にアクセスするなら、そんな演算子使わなくてもhoge.fugaでいいじゃないか」と思うかもしれません。

const hoge = {
    foo: 1,
    bar: 'test'
};

const fuga = hoge.foo;

つまり上記のように、hoge.fooと記述することでhogeオブジェクトのfooプロパティにアクセスするわけです。もちろんですが、上記のコードで実際にhoge.fooに問題なくアクセスできるため、エラーは発生せず処理が正常終了します。では、ここでhogeオブジェクトに存在しないプロパティを指定した場合は、どういう動作になるか考えてみます。

const hoge = {
    foo: 1,
    bar: 'test'
};

const piyo = hoge.baz;

hoge.bazと書いてみました。hogeオブジェクトにはbazというプロパティが存在しないため、この場合だとundefinedが返ってきます。JSでは、undefinednullのプロパティにアクセスするとエラーが発生します。

const hoge = {
    foo: 1,
    bar: 'test'
};

const fuga = hoge.foo; // これはOK
const piyo = hoge.baz; // これはNG

alert(piyo); // TypeError: Cannot read properties of undefined

上記ではundefinedのプロパティをアクセスしてエラーが発生していますが、nullのプロパティでも同様にエラーが発生します。コーディングミスなら修正すればいいわけですが、何らかの理由でプロパティの存在が保証されていなくても、場合によっては処理を進める必要があるかもしれません。

また、これとは別なケースとして、「アクセスしたプロパティが存在しないことは普段に限り正常である」という場合、エラー発生を避けつつundefinednullを代入したいというニーズも考えられるかもしれません。このようなニーズを実装するには、undefinednullではないかチェックする必要があります。ざっくり書いてみます。

const hoge = {
    foo: 1,
    bar: 'test'
};

const fuga = hoge.foo === undefined || hoge.foo === null ? undefined : hoge.foo;

たとえば上記のようにチェックし代入することになりますが、お世辞にも見やすいコードとは言えないでしょう。チェックする対象が複数ある場合は、入れ子構造になったりして余計に可読性が下がります。

つまり、チェックすればエラーは発生しなくなりますが、チェックのコードを記述することで可読性が損なわれてしまいます。そこで、「チェックはしたいが可読性は犠牲にしなくない」というニーズが出てくるわけです。このニーズを満たすために、オプショナルチェーン演算子を使います。

const hoge = {
    foo: 1,
    bar: 'test'
};

const fuga = hoge?.baz; // ここ

繰り返しになりますが、オプショナルチェーン演算子はオブジェクト名?.プロパティ名の形で記述します。この形で記述することで、「プロパティにアクセスする」ことと「アクセスしたプロパティがnullなどの場合に発生するエラーを回避する」ことという2つを両立できます。?.に先行する変数やオブジェクトがundefinedなどである場合、その先のプロパティは評価されることがないため、エラーは発生しません。このように、オブジェクトがundefinedであるか否かを明示的にチェックすることなく、プロパティに対し簡単にアクセスできるようになります。

関数に対して使う

const func = (val: number) :number=> {
    return val * 2;
};

const funcNull = () => undefined;

const result = func?.(1);

const resultNull = funcNull?.();

console.log(result);
console.log(resultNull);

undefinedを返す関数について、オプショナルチェーンを使ってみます。なお、書き方はfunction?.()のように、関数名と引数カッコの間に?.を記述します。

2 
undefined 

オプショナルチェーンはプロパティと同様、関数に対しても利用可能です。ある関数についてundefinedが返ってくる可能性を含んでいる場合、関数に対しオプショナルチェーン演算子を付与することで明示的に戻り値をチェックする必要がなくなります。

メソッドに対して使う

const obj = {
  func: (i: number) => {
    return i*2 
  },
  funcNull: () => undefined
};

const result = obj.func(1);
const resultNull = obj.funcNull?.();

console.log(result);
console.log(resultNull);

メソッドも関数同様にオプショナルチェーンが利用できます。ここではundefinedを返すメソッドを定義して、そのメソッドにオプショナルチェーンを使ってみます。

2 
undefined 

使い方の感覚は関数に対するケースと同じですね。

undefinednullが返ってきたときのためにNull合体演算子

undefinednullが返ってきた場合の初期値を設定したければ、Null合体演算子を利用すると良いでしょう。まーたコイツもググラビリティがすこぶる低い演算子なんスけどね。なにせ??です。

const func = (arg: any) => {
    const foo = arg ?? 'default string';

    console.log(foo);
}

func(1);
func('hoge');
func(undefined);
func(null);

Null合体演算子は、演算子の左辺がnullあるいはundefinedである場合は演算子の右辺に設定した値を返し、それ以外の場合は左辺の値を返します。論理OR演算子||とは異なり、Null合体演算子はnullundefinedのみが右辺の値を返す対象です。空文字やゼロは対象外ですので、Falsyな値全般を対象とするような使い方はできません。そのため、空文字やゼロをFalsy(偽値)ではなく「使用可能な値」として扱いたい場合、論理OR演算子ではなくNull合体演算子を利用することになります。そして、前述のとおりNull合体演算子はnullundefinedを対象とするため、エラーを発生させずundefinedを返すオプショナルチェーンとすこぶる相性が良いです。

const func = (arg: any) => {
    const foo = arg ?? 'default string';

    console.log(foo);
}

const hoge = {
    foo: 1,
    bar: 'ham'
};

func(hoge?.foo);
func(hoge?.bar);
func(hoge?.baz);

上記のように、オプショナルチェーンとNull合体演算子を組み合わせることで、nullundefinedだったときのデフォルト値を設定できます。

const func = (arg: any) => {
    const foo = arg ?? 'default string';

    console.log(foo);
}

const funcNull = () => undefined;

func(funcNull?.());

関数に関しても同様です。

まとめ

安全にプロパティを参照できる、便利な方法です。ネストが可能で関数にも利用できるように守備範囲が広いうえ、Null合体演算子と組み合わせることでデフォルト値の設定も可能と使い勝手が抜群です。JSでありがちな「存在しないプロパティに誤アクセスしちゃってエラーが起きる」問題を回避するためにも、積極的に使っていきたいですね。

reference

  1. オプショナルチェーン
  2. オプショナルチェーン(optional chaining)
  3. JavaScriptのオプショナルチェーン演算子の使い方!
  4. オプショナルチェーン
  5. Null合体演算子
comments powered by Disqus