オプショナルチェーン演算子ってのは、JavaScript(以下、JS)にありがちな「一見すると何なのかよくわからない」かつ「ググラビリティが低い」系の記述方法です。個人的に見た目が似たようなタイプだと思っている三項演算子とかだと、まだなんとなーく解読できそうな見た目だったりしますが、この演算子は初見だと何やってるのかいまいちわかりにくい演算子です。
なにせ、hoge?.foo
みたいな見た目なので・・・もう少しこう何というか、手心というか_(:3」∠)_
ただ、オブジェクトにプロパティをいろいろ付与しがちなJSだと、この演算子の使いでが結構あるので覚えておいて損はないです。というか、得しかないかも。
そんなコイツは一体なんなのかと言うと、undefined
やnull
のプロパティに誤アクセスした際、発生するエラーを回避しながら可動性を確保できる書き方です。つまり、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では、undefined
やnull
のプロパティにアクセスするとエラーが発生します。
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
のプロパティでも同様にエラーが発生します。コーディングミスなら修正すればいいわけですが、何らかの理由でプロパティの存在が保証されていなくても、場合によっては処理を進める必要があるかもしれません。
また、これとは別なケースとして、「アクセスしたプロパティが存在しないことは普段に限り正常である」という場合、エラー発生を避けつつundefined
やnull
を代入したいというニーズも考えられるかもしれません。このようなニーズを実装するには、undefined
やnull
ではないかチェックする必要があります。ざっくり書いてみます。
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
使い方の感覚は関数に対するケースと同じですね。
undefined
やnull
が返ってきたときのためにNull合体演算子undefined
やnull
が返ってきた場合の初期値を設定したければ、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合体演算子はnull
とundefined
のみが右辺の値を返す対象です。空文字やゼロは対象外ですので、Falsyな値全般を対象とするような使い方はできません。そのため、空文字やゼロをFalsy(偽値)ではなく「使用可能な値」として扱いたい場合、論理OR演算子ではなくNull合体演算子を利用することになります。そして、前述のとおりNull合体演算子はnull
とundefined
を対象とするため、エラーを発生させず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合体演算子を組み合わせることで、null
やundefined
だったときのデフォルト値を設定できます。
const func = (arg: any) => {
const foo = arg ?? 'default string';
console.log(foo);
}
const funcNull = () => undefined;
func(funcNull?.());
関数に関しても同様です。
安全にプロパティを参照できる、便利な方法です。ネストが可能で関数にも利用できるように守備範囲が広いうえ、Null合体演算子と組み合わせることでデフォルト値の設定も可能と使い勝手が抜群です。JSでありがちな「存在しないプロパティに誤アクセスしちゃってエラーが起きる」問題を回避するためにも、積極的に使っていきたいですね。