そろそろJavaScriptに採用されそうなOptional Chainingを今さら徹底解説

みなさん、Optional Chaining使ってますか？　私は先日出たTypeScript 3.7 Betaを小さいプロジェクトに導入して使ってみました。これはとても快適ですね。

例によって、Optional ChainingはECMAScriptに対するプロポーザルの一つです。つまり、もうすぐ入りそうな**JavaScriptの新機能**です。プロポーザルはたくさんの種類がありますが、その中でもOptional Chainingはその高い有用性からこれまで多くの注目を集めてきました。Optional Chainingは2019年6月のTC39ミーティングでStage 3に上昇し、いよいよ正式採用が近く期待も高まってきたところです。TypeScript 3.7にも導入されたため、TypeScriptユーザーの方々は11月上旬に正式リリースが予定されているTypeScript 3.7を今か今かと待ち構えていることでしょう（筆者のようにフライングしてBetaで使い始めている人もいるかもしれません）。また、Babelのプラグインも前々から存在したため熱心な方々はもう使っているかもしれません。

となると、巷に、あるいはこのQiitaにもOptional Chainingに関する記事がいくつもあります。そこで自分もOptional Chainingの解説がしたくなってできたのがこの記事です。「もうQiitaにOptional Chainingの記事あるのに今さら？」と思われるかもしれませんが、

**この記事を読めばOptional Chainingの全てがわかります。**（強気）

期待してお読みください。なお、Optional Chainingに関する一次的な情報源は（ECMAScriptに正式採用されるまでは）以下のプロポーザルです。一次情報にあたるのが好きな方はぜひ目を通してみましょう。

- [Optional Chainingのプロポーザル](https://github.com/tc39/proposal-optional-chaining)

## Optional Chainingの基本

**Optional Chaining**とは、`?.`という新しい構文です。

もっとも基本的な用法はプロパティアクセスの`.`の代わりに`?.`を使うものです。`obj?.foo`とした場合、`obj`がnullishな値（`null`または`undefined`）の場合は結果が`undefined`となり、そうでない場合は`obj.foo`が結果となります。

つまり、`obj?.foo`というのはおおよそ`obj != null ? obj.foo : undefined`と同じです[^slight_semantics]。

[^slight_semantics]: おおよそというのは、`obj`を評価するのは1回だけであるとか、`document.all`との兼ね合いといったことを指しています。

たとえ`obj`が`null`だったとしても`obj?.foo`は`null`ではなく`undefined`になります。間違えやすいので注意しましょう[^null_undefined]。

[^null_undefined]: `obj`が`null`のときは`obj?.foo`が`null`になってほしいという意見もありそうですが、このときの結果が`undefined`である理由付けは一応あります。それは「`obj?.foo`は`obj`についての情報を得るためのものではなく`foo`の情報を得るためのものである。`null`も`undefined`もどちらも`foo`を持たないという点で同じなのだから、結果は`undefined`に統一して`obj`の情報が伝播しないようにすべきである」というものです。個人的にはまあ一理あると思っています。また、JavaScript本体の言語仕様はそもそも`null`よりも`undefined`に偏重しているため（DOMは逆に`null`に寄っていますが）、`undefined`に統一されるのもまあ妥当に思えます。

### 背景

この機能の背景には、**`undefined`または`null`に対してプロパティアクセスを行うとエラーになる**という言語仕様があります。すなわち、`obj`が`null`とかの時に`obj.foo`を実行するとエラーとなります。

ちなみに、プロパティアクセスがエラーとなるのはこの場合のみで、それ以外の場合（`foo`というプロパティを持たないオブジェクトや、あるいは数値や文字列といった他のプリミティブ）はエラーにはなりません[^property_access_error]。`foo`というプロパティがない場合、エラーにはならず`obj.foo`が`undefined`となります。

[^property_access_error]: 厳密に言えば、ゲッタがエラーを発生させた場合や`Proxy`の場合など、プロパティアクセスに起因してエラーになる可能性は他にもあります。とはいっても、今はそういう話をしているのではないことはお分かりですよね。

このことから、nullishかもしれない値`obj`に対しては`obj.foo`のようなプロパティアクセスをいきなり行うのは危険であり、前述のように`obj != null`というようなチェックが不可欠でした。逆に言えば、nullishな値さえ排除すればとりあえずプロパティの値を取得するところまでは漕ぎ着けることができます（取得できた値が`undefined`かもしれませんが）。

ということで、nullishな値に対するチェックをより簡単に行えるようにするのが`?.`構文ということになります。`obj?.foo`ならば、`obj`がnullishな値であってもエラーが発生せず、代わりに`undefined`が結果となります。これにより、残りの場合と一緒に扱うことができるようになるケースは結構多いと思われます。特に、[Nullish Coalescing](https://github.com/tc39/proposal-nullish-coalescing)（`??`演算子）と組み合わせることで、明示的に条件分岐を書かずにnullishな値を適切に処理することができるケースが増えるでしょう。これはとてもうれしい点です。

[プロポーザル](https://github.com/tc39/proposal-optional-chaining)によれば、`?.`に類似の構文は以前からGroovy, C#, Swift, CoffeeScriptなどに存在していました。この中ではGroovyが最も古く、バージョン1からすでに存在していたようです。その次が恐らくCoffeeScriptで、これはAltJSであるということもあり今回のプロポーザルに直接的な影響を与えています。

### 3種類の構文

厳密には、今回のプロポーザルで`?.`に関連する構文が3種類追加されます。

- `obj?.foo`
- `obj?.[expr]`
- `obj?.(arg1, arg2)`

一番上はこれまで説明していたやつです。二番目は、お分かりの通り`obj[expr]`用の構文で、これを`obj?.[expr]`に変えることで`obj`がnullishの場合はエラーではなく`undefined`が返るようになります。

三番目はオプショナル関数呼び出し構文です。この例は引数2つですがもちろん何個でも構いません。`obj(arg1, arg2)`は`obj`がnullishのときはやはりエラーとなりますが、`obj?.(arg1, arg2)`は`obj`が`nullish`のときは何も行わずに`undefined`となります。なお、`obj`がnullishではないが関数でもない場合は依然としてエラーになりますので注意しましょう。

3種類が一貫して`?.`というトークンを持っているので分かりやすくていいですね。ただ、`?.`の直後に`[ ]`とか`( )`のような括弧が来る構文が気持ち悪いという人もいるようです（詳しくはすこし後で解説します）。

ちなみに、代入には対応していません。つまり、`obj?.foo = 123` （`obj`が存在するときのみ`foo`プロパティに`123`を代入する）のようなことはできません。

また、ES2015では`` obj.foo`123` ``のようにタグ付きテンプレートリテラルの記法で関数を呼び出すことが可能になりましたが、これはOptional Chainingのサポート外です。つまり、`` obj?.foo`123` ``のようなことはできません。`` func?.`123` ``もだめです。

### 短絡評価

Optional Chainingを語る上で外せないのが**短絡評価**です。実はここがOptional Chainingを理解する上で肝となる部分です。将来足元を掬われないように今のうちにしっかりと理解しておきましょう。

#### 短絡評価とは

短絡評価という単語は、`&&`や`||`に絡んで聞いたことがある方が多いでしょう。これらの演算子は、左側を評価した時点で結果が確定したのであれば右側が評価されません。これを短絡評価と呼びます。

```js
const foo = 123;

// fooが真なので func() は呼び出されない
const v1 = foo || func();
```

上の例では、`||`の結果は左側の`foo`を評価した時点で確定します。よって、右側は意味がないのでそもそも評価されないことになります。

#### `?.`の短絡評価

`?.`の短絡評価についても基本は変わりません。**左側を評価した時点で結果が確定するならば、右側は評価されない**というのが原則です。

`?.`の場合は、左側がnullishな値だと判明した時点で結果が`undefined`に確定します。よって、その場合は右側は評価されません。このことは例えば次のような場合に影響があります。

```js
const foo = obj?.[getKey()];
```

この例では、`obj`がnullishならば`foo`には`undefined`が入り、そうでなければ`foo`には`obj[getKey()]`が入ります。キーの名前（`[ ]`の中身）を得るには`getKey()`という関数呼び出しを評価する必要があります。

ポイントは、短絡評価により**`obj`がnullishな値ならば`getKey()`は呼び出されない**という点です。まあ、これは次と同じと考えれば自然な動作ですね。この場合も、`getKey()`が呼び出されるのは`obj`がnullishでないときだけです。

```js
const foo = obj != null ? obj[getKey()] : undefined;
```

関数呼び出しの構文でも同じです。次の例では、`func`がnullishな値のときは`foo()`, `bar()`, `baz()`は計算されません。

```js
func?.(foo(), bar(), baz())
```

### オプショナルチェーンの短絡評価

短絡評価の応用例として、次のような場合を考えてみてください。

```js
obj?.foo.bar.baz
```

次の3種類の場合にこれの評価結果はどうなるでしょうか。

1. `obj`が`{ foo: { bar: { baz: 123 } } }`の場合。
2. `obj`が`{ foo: { bar: null } }`の場合。
3. `obj`が`undefined`の場合。

**正解**はこうです。

1. `123`になる。
2. `obj?.foo.bar`が`null`になり、`null`の`baz`プロパティを読もうとしてエラーが発生する。
3. `undefined`になる。

特に3がポイントです。`obj`がnullishな値だったことにより、そのあとの部分全部（`?.foo.bar.baz`）が無視されます。

これに関しては、次のような<font color="blue">**誤解**</font>が発生しやすいので注意が必要です。

1. `123`になる。
2. エラーになる。
3. <font color="blue">エラーになる。（誤解）</font>

3の<font color="blue">**誤解**</font>は次のような考え方をすると発生しがちです。

- `obj?.foo.bar.baz`の`obj?.foo`部分がまず評価されて`undefined`になる。
- すると`undefined.bar.baz`が評価されることになる。
- `undefined`の`bar`プロパティを読もうとしてエラーになる。

繰り返しますが、この誤解を避けるために抑えるべきポイントは`obj?.foo.bar.baz`で`obj`がnullishな値の場合は`?.foo.bar.baz`全体が飛ばされるということです。

ちなみに、このように`?.`から始まるプロパティアクセス（または関数呼び出し）の列のことを**オプショナルチェーン (optional chain)**と呼びます。`?.foo.bar.baz`は一つのオプショナルチェーンです。この用語を使うと、`?.`の評価は**左側がnullishな値の場合はオプショナルチェーン全体を無視して`undefined`を返す**ものだと言うことができます。

オプショナルチェーンは`[ ]`によるプロパティアクセスや`()`による関数呼び出しを含むことができるので、次のようなものも一つのオプショナルチェーンです。

```js
?.foo.bar["hoge fuga"](1, 2, 3).baz
```

### オプショナルチェーンと括弧

上の例を少し変えてこうしてみましょう。

```js
(obj?.foo.bar).baz
```

この場合、括弧で区切られているので`?.foo.bar`までがオプショナルチェーンであり`.baz`はチェーンに含まれません。よって、`obj`がnullishな値だった場合はまず`obj?.foo.bar`が`undefined`に評価され、`undefined.baz`を計算しようとしてエラーになります。

このように、**括弧によってオプショナルチェーンが区切られてプログラムの意味が変わることがある**点は要注意です。これはオプショナルチェーンの新しい特徴です。従来は`obj.foo.bar.baz`を`(obj.foo.bar).baz`に変えても意味は変わりませんでした。

オプショナルチェーンに間違えて括弧をはさむ人はあまり居ないと思いますが、3ヶ月に1回くらいこんな罠を踏むかもしれませんから気をつけましょう。

### 複数のオプショナルチェーン

ところで、次のような式を考えることもできます。

```js
obj?.foo.bar?.baz(123)
```

`?.`が2箇所に出てきました。この式はどのように解釈できるでしょうか。

実は、これは`obj`のあとに**2つのオプショナルチェーン**が続いている形になっています。1つ目は`?.foo.bar`で、2つ目が`?.baz(123)`です。

先ほどオプショナルチェーンという概念を紹介しましたが、これは先頭のみに`?.`が来るものであり、次の`?.`が来た時点でそこから先は次のオプショナルチェーンになります。上の式は以下のように括弧を付けても同じことです。

```js
(obj?.foo.bar)?.baz(123)
```

途中で`undefined`や`null`が発生する可能性があるときは複数のオプショナルチェーンを繋げる意味もあります。例えば`obj`が`{ foo: { bar: null } }`だった場合を考えましょう。このとき、上の式の結果は`undefined`となります。まず`obj?.foo.bar`が`null`になり、それに対して次のオプショナルチェーン`?.baz(123)`が適用されますが、`?.`の左は`null`なので全体の結果は`undefined`となります。

一方で、`?.`がひとつだけの場合は違った結果になります。上の`obj`に対して`obj?.foo.bar.baz(123)`を評価した場合を考えると、これは`obj?.foo.bar`まで評価して`null`を得たところでそれの`baz`プロパティを取得しようとしてエラーになります。

### `?.[ ]`と`?.( )`

`?.`という構文は一貫しているとはいえ、特に`obj?.[expr]`とか`func?.()`という構文は`.`の後に括弧が来る点が気持ち悪いと思えるかもしれません。どちらかというと`obj?[expr]`とか`func?()`のほうがきれいです。

しかし、もちろんそうはできない理由がありました。それは**条件演算子** `cond ? expr1 : expr2`の存在です。どちらも`?`を使っていることがあり、両方の構文があるとたいへん紛らわしくなります~~（`:`があるので多分文法が曖昧になるというわけではなさそうですが）~~ [^cond_ambiguity]。

[^cond_ambiguity]: [コメントで曖昧さがある場合を指摘いただきました](https://qiita.com/uhyo/items/6cd88c0ea4dc6289387a#comment-286fd39157500a1b0b17)。

プロポーザル文書に載っている例を引用します。

```js
obj?[expr].filter(fun):0
```

4文字目の`?`は条件演算子の`?`なのですが、`:`を見るまでは`obj?[expr].filter(fun)`というオプショナルチェーンである可能性を捨て切れません。このように判断を遅延しなければいけないのは人間にも処理系にも負担になります。

これを避けるために`?.`というトークンを使っているのです。

### `?.`と数値リテラル

実は、`?.`を用いてもなお文法上の問題が多少残っています。それは、`.1`のような数値リテラルが関わる場合です。実は数値リテラルは`.1`のようにいきなり少数点から始めることができます。これは`0.1`と同じ意味です。これまたプロポーザルから引用しますが、次のような場合が問題になります。

```js
foo?.3:0
```

これの正しい解釈は`foo ? .3 : 0`、つまり条件演算子です。コード中に`?.`の並びがありますが、これはoptional chainingの構文ではなく`?`と`.3`が並んだものです。

このことを表現するために、「Optional Chainingの`?.`構文の直後に数字（`0`〜`9`）が来ることはない」という規則が用意されています。これにより、`?.3`という並びを見た時点で即座にこれは`?. 3`ではなく`? .3`であることが判断できるようになっています。

そもそも`obj.3`のようなプロパティアクセスは文法上不可能ですから`obj?.3`と書いてあっても`obj ?. 3`と解釈する必要はないように思えます。それにも関わらず上記のような規則があるのは、極力分岐を減らしてパーサーの負担を減らすためでしょう。

そもそも、JavaScriptの文法というのは原則として前から順番に読んでいけば常に解釈が1通りに定まり、複数の可能性が同時に存在しないように定義されています（これが達成できていないところもあるのですが、[そういうところは仕様上ではカバー文法を用いて明示的な配慮がされています](https://www.ecma-international.org/ecma-262/10.0/index.html#prod-CoverParenthesizedExpressionAndArrowParameterList)）。

今回も、パーサーがプログラムを前から読んで`?.`まで読んだ瞬間に、これが`?.`というトークンなのかそれとも`?`が条件演算子でその後に何か別の`.`が続いているのかを決定できるのが理想です。ただ、`foo?.3:0`と`foo?.bar`というプログラムが両方存在する可能性がある以上、これだけの情報からではこれは不可能です。

しかし、実は1文字先読みをすることでこれが可能になります。つまり、`.`の次の文字が数値ならばその時点で`?.`の`?`が条件演算子であることが確定し、そうでなければ`?.`はOptional Chainingの開始であることが確定します。

一般に長く先読みするほどパースが大変になりますが、まあ1文字先読みくらいなら許容範囲です。


---

以上がOptional Chainingの基本でした。おおよそ[プロポーザルの文書](https://github.com/tc39/proposal-optional-chaining)に書いてあることを説明した感じです。この文書も分かりやすく書かれていますので一度目を通してみてもよいかもしれません。

## 他の言語との比較

CoffeeScriptを始めとして、Optional Chainingに類似の言語機能をすでに持っているプログラミング言語はいくつか存在します。ここでは、他の言語とJavaScriptのOptional Chainingとの違いや共通点を明らかにします。

以下の言語はプロポーザルの文書に言及があったものを列挙しています。他にこんな言語にもあるよという情報は大歓迎です。

### Groovy

一番手は確認されている中で最も古くから`?.`を持つGroovyです。Groovyではこれは[Safe Navigation Operator](https://groovy-lang.org/operators.html#_safe_navigation_operator)と呼ばれています。

シンプルに`obj?.foo`という形がサポートされています。`?.`の挙動は言語によって多少異なり、上記で説明した短絡評価があるかないかの2種類に大別されるのですが、Groovyはどちらなのかよく分かりませんでした。調べようとも思いましたがプロポーザル文書にも「よく分からなかったわ」と書いてあるので多分大変なのだろうと思い調べていません。これに関する情報をお持ちの方はぜひお寄せください。

### CoffeeScript

次はCoffeeScriptです。CoffeeScriptではこの機能は[Existential Operator](http://coffeescript.org/#existential-operator)と呼ばれ、JavaScriptと同じく3種類の構文を持ちます。それぞれの構文は以下のように表現されます。

- `obj?.foo`
- `obj?[expr]`
- `func?(arg)`（または関数呼び出しの括弧を省略して`func? arg`）

まずは構文を比較します。CoffeeScriptでも`?.`という演算子を用いて`obj?.foo`のように書くことができます。ただ、`[ ]`と`( )`に関してはJavaScriptとは異なり、`obj?[expr]`や`func?(arg)`のように書くことができました。

JavaScriptとは異なりこれらの場合に`?.[expr]`としなくても良かった理由は、CoffeeScriptが条件演算子`?:`を採用していないからです。代わりに`if`が式となっており、条件演算子のように使用できます。

挙動については、現在のJavaScriptのものと基本的に同じです。つまり、`obj?.a.b.c`で`obj`が`undefined`の場合はチェーン全体が飛ばされるという挙動をします。短絡評価についても同じであるほか、括弧でチェーンを切ることができる点も同じです。

ただ、CoffeeScriptでは今のJavaScriptにはないいくつかの追加機能を備えていました。ひとつはオプショナルな代入です。

```coffeescript
obj?.foo = 123
```

このようなプログラムが可能であり、これは`if (obj != null) obj.foo = 123;`とおおよそ同じ意味でした。

また、タグ付きテンプレートリテラルの関数部分でも`?.`が使用可能です。

```coffeescript
obj?.foo"""123"""
```

JavaScriptでは、前者は仕様が複雑になることから、後者はユースケースの欠如から採用されていない仕様です。CoffeeScriptの大胆さと言語デザインが伝わってくる例ですね。

長くなりましたが、他の言語はあまり詳しいわけではないのでさらっと流します。

<h3>C#</h3>

C#ではこれは[Null-conditional operator](https://msdn.microsoft.com/en-us/library/dn986595.aspx)（Null条件演算子）と呼ばれています。2015年リリースのC# 6で追加されたようです。

C#では`obj?.foo`と`obj?[expr]`の2種類の構文がサポートされています。C#にも条件演算子`? :`があるはずですが、JavaScriptとは異なり`obj?[expr]`の形が採用されています。これは、C#では`[expr]`のような式が存在しないことが理由と推測されます。`?`の後が`[`である時点で`?`が条件演算子であることが確定できるのです。JavaScriptと同様に1文字先読みで済んでいます。

短絡評価周りの挙動も、基本的に今回解説したJavaScriptのものと同様です。

なお、C#は（というかほとんどの言語は）`undefined`と`null`が別々にあるみたいな意味不明な状況にはありません。C#では`null`があり、`?.`の左辺が`null`のときに結果が`null`になるという仕様です。

オプショナル関数呼び出し`func?.()`にあたる構文はありませんが、C#ではデリゲート（thisを覚えている関数オブジェクトのようなものらしいです）が`Invoke`を持っており、`func?.Invoke()`のようにして代替可能です。JavaScriptでも関数オブジェクトが`call`メソッドなどを持ってはいますが、thisを渡さないといけないせいで微妙に使い勝手が悪くなっています。

`obj?.foo = 123`はサポートされていません。

### Swift

Swiftではこの機能は[Optional Chaining](https://docs.swift.org/swift-book/LanguageGuide/OptionalChaining.html)です。同じ名前ですね。Swiftでは`nil`がこの機能の対象です。

Swiftでも構文はやはり`obj?.foo`と`obj?[expr]`です。オプショナル関数呼び出しは無いようです。

挙動はJavaScriptと同じく、チェーンの短絡評価および括弧でチェーンを切る挙動ができます。

また、代入におけるOptional Chaining（`obj?.foo = 123`）もサポートしています。面白い点はこの代入式の返り値が`Void?`型であり、代入が成功したか失敗したかが返り値から分かるようになっている点ですね。

### Kotlin

Kotlinも`?.`演算子を持ち、これは[Safe Call](https://kotlinlang.org/docs/reference/null-safety.html#safe-calls)と呼ばれているようです。

Kotlinでは、これまでの言語とは異なりオプショナルチェーンの概念は存在しません。`obj?.foo.bar`は`(obj?.foo).bar`と同じであり、`obj`が`null`の場合は`.foo`は飛ばされますが`.bar`は飛ばされません。これは`obj?.foo?.bar`と書く必要があります（Kotlinはいわゆる`null`安全な型システムを持っているので、こう書かないとコンパイルエラーとなります）。

なお、`obj?[expr]`に相当する記法は無いようです。例えば`List<Int>?`型から`Int?`を取り出したい場合、`list?.get(0)`[^kotlin_get]のようにするか`list?.let { it[0] }`とする必要があります（あまり自信がないので間違っていたらぜひ訂正をお願いします）。

`obj?.foo = 123`は可能です。

[^kotlin_get]: `obj[expr]`は`obj.get(expr)`の糖衣構文。

### Dart

[Conditional Member Access](https://dart.dev/guides/language/language-tour#other-operators)と呼ばれ、`?.`演算子のみが存在するようです。代入も可能です。

また、Kotlinと同様に一段階のみのサポートです。

### Ruby

Rubyでは[Safe Navigation Operator](https://ruby-doc.org/core-2.6/doc/syntax/calling_methods_rdoc.html#label-Safe+navigation+operator)と呼ばれており、Ruby 2.3で導入された機能のようです。

Rubyではこれは`&.`という名前です。これまでの言語で唯一`?`を含んでいませんが、まあこれは仕方ありませんね。Rubyは識別子（メソッド名など）に`?`や`!`を含むことができる言語なので、演算子に`?`を使うのはさすがに都合が悪そうです。

Kotlinなどと同様に`&.`は一段階しか作用しません。Rubyはドキュメントにこのことが明記してあってたいへんありがたいですね。

### 余談：Elm

ちょっと趣向を変えて、というか趣味に走っていますが、関数型言語との比較もしてみます。そもそも関数型言語はオブジェクトとかプロパティという概念を持たないこともあるので比較の意味がそこまで大きいわけではありません。なので余談ということにしてみました。

さて、値が無いかもしれない（`null`かもしれない）という状況に対して、これから説明するように関数型言語はかなり異なる方法で対処します。

関数型言語の場合しっかりとした代数的データ型を備えていることが多く、`null`のような概念の代わりになるものとして`Maybe`型のようなデータ構造を持つのが典型的です。Elmの[Maybe型](https://package.elm-lang.org/packages/elm/core/latest/Maybe)はHaskellと同じ定義で、例えば`Maybe Int`型は`Just 42`のような`Int`型の値をラップした値と`Nothing`から成ります。

また、Elmの場合はJavaScriptのオブジェクトに比較的近い概念としてレコードというものがあります。レコードはこのように使用します。

```elm
import Html exposing (text)
-- Person 型を定義（実態はただのレコード型）
type alias Person =
  { name: String
  , age: Int
  }

-- Person型の変数pを定義（型宣言は省略可）
p: Person
p =
  { name = "John Smith"
  , age = 100
  }

main =
  text (p.name) -- "John Smith" が表示される
```

`Person`が存在するかもしれないししないかもしれないという状況は`Maybe Person`型で表現します。

```elm
p1: Maybe Person
p1 = Just
  { name = "John Smith"
  , age = 100
  }
  
p2: Maybe Person
p2 = Nothing

main =
  text (p1.name) -- これはコンパイルエラー
```

`Maybe Person`は`Person`とは別の型でありレコードではないため`p1.name`のようなアクセス方法はできません。

また、Elmは`?.name`のようなことができる機能はありません。筆者はElmに詳しくありませんが、やるとしたら恐らくこうでしょう。

```elm
n1 = Maybe.map .name p1 -- Just "John Smith"
n2 = Maybe.map .name p2 -- Nothing
```

`Maybe.map`は与えられた関数を`Just`の中身に適用する関数です（`Nothing`のときはそのまま`Nothing`が返る）。`.name`はこれでひとつの関数であり、与えられたレコードの`name`フィールドを返します。

ポイントは、無いかもしれない値（`Maybe Person`型の値）を操作するにあたって2つの一般的な関数（`Maybe.map`と`.name`）を用いて対処している点です。むやみに演算子を増やすよりも関数の組み合わせで対処する点に関数型言語らしさが現れています。真に何もない値であり関数・メソッド等のサポートを受けにくい`null`と比べると、`Maybe`は代数的データ型を用いて表現される値であり`Maybe.map`に代表されるような標準ライブラリレベルでのサポートが受けやすい点が大きく異なっています。


ちょっと話が横道に逸れましたが、以上が他の言語との比較でした。他の言語の情報をお持ちの方はお寄せいただけるとたいへん幸いです。

## TypeScriptとOptional Chaining

さて、ではJavaScriptに話を戻しましょう。……と言いたいところですが、次はTypeScriptに話を移します。TypeScriptは言わずと知れたJavaScriptの型付きバージョンです。

TypeScriptは、プロポーザルがStage 3になったらその機能を導入するという方針があるようです。ということで、Optional ChainingがTypeScriptに導入されるのは11月リリースのTypeScript 3.7です。現在すでにベータ版が出ており、これでTypeScriptのOptional Chainingサポートを体験できます。

ここではTypeScriptにおけるOptional Chainingの挙動を解説します。もはやTypeScriptがJavaScript開発における必須ツールとなりつつある今日この頃ですから、JavaScriptの新機能とあればTypeScriptにどう影響するのか気になるのは必然です。ということで、この記事では欲張りなことにTypeScriptにも手を伸ばして解説します。

とはいえTypeScriptなんか興味ありませんよという硬派（安全性的にはむしろ軟派？）な方もいるでしょうから、そのような方は次のセクションまで飛ばしましょう。また、TypeScriptの用語で分からないところがあれば[TypeScriptの型入門](https://qiita.com/uhyo/items/e2fdef2d3236b9bfe74a)が参考になるかもしれません（宣伝）。

では、さっそく`?.`の例をお見せします。

```ts
interface HasFoo {
    foo: number;
}

const obj: HasFoo | undefined = Math.random() < 0.5 ? { foo: 123 } : undefined;

// これはエラー (objがundefinedかもしれないので)
const v1 = obj.foo;

// これはOK（v2はnumber | undefined型）
const v2 = obj?.foo;
```

`HasFoo`型は、`foo`というプロパティを持つオブジェクトの型です。今回は変数`obj`を`HasFoo | undefined`型として宣言しました。これは、`obj`の中身は`HasFoo`型のオブジェクトかもしれないし`undefined`かもしれないということです。

この`obj`に対して`obj.foo`とすると、TypeScriptにより型エラーが発生します。これは、`obj`が`undefined`かもしれない状況で`obj.foo`を実行するとエラーになるかもしれなくて危険だからです。TypeScriptは型エラーにより、そのような危険なコードを事前に警告してくれます。

一方、`obj?.foo`は型エラーになりません。これは、`?.`ならばたとえ`obj`が`undefined`でもエラーが発生することはなく安全だからです。

その代わり、`obj?.foo`の結果は`number | undefined`型となります。これは、`number`型かもしれないし`undefined`型かもしれないという意味です。実際、`obj`が`undefined`のときは`obj?.foo`の結果は`undefined`になるし、`obj`が`undefined`でないときは（`obj`が`HasFoo`型になるので）`obj?.foo`は`number`になるためこの結果は妥当です。

### 型推論のしくみ

上では言葉でそれっぽい説明をしましたが、型推論の挙動を整理するのはそれほど難しくありません。これに関しては[TypeScriptの当該プルリクエスト](https://github.com/microsoft/TypeScript/pull/33294)も参考になるでしょう。

例えば、`expr?.foo`という式の型を推論するにあたってはおよそ以下のような過程を減ることになります。

1. 普通に`expr`の型を推論する（`T`とする）。
2. `T`が`null`や`undefined`を含むunion型の場合：
    1. `T`から`null`と`undefined`を除いた型`T2`を作る。
    2. `T2`の`foo`プロパティの型`U`を得る。（`foo`プロパティが無ければ型エラー）
    3. `expr?.foo`の型を`U | undefined`とする。
3. `T`が`null`や`undefined`を含むunion型ではない場合：
    1. 普通に`expr.foo`の型を返す。（無いなら型エラー）

要するに、`expr`から一旦`null`や`undefined`の可能性を除いて考えて、もしそういう可能性があるなら結果の型に`undefined`をつけるということです。

### never型に関する注意

知らないと少し混乱するかもしれない例がひとつありますのでここで紹介しておきます。それは、`expr?.foo`で`expr`がただの`undefined`型（あるいは`null`型とか`undefined | null`型）だった場合です。

```ts
const obj = undefined;

// 型エラーが発生
// error TS2339: Property 'foo' does not exist on type 'never'.
const v = obj?.foo;
```

この例では`obj`は`undefined`型です（もはやオブジェクトではないので変数の命名が微妙な気もしますが）。したがって、`obj?.foo`は常に結果が`undefined`となり、`foo`プロパティへのアクセスが発生することはありません。

となると`obj?.foo`の型は`undefined`型になりそうな気がしますが、実際はそうではありません。というか、実はこの式は型エラーとなります。

そもそも、`obj`が`undefined`であると判明しているのであれば`obj?.foo`は絶対に`undefined`にあるのであり、わざわざこんな書き方をする意味はありません。何かを勘違いしている可能性が非常に高いでしょう。その意味では、これが型エラーになるのはどちらかといえば嬉しい挙動です。

問題なのはエラーメッセージです。エラーメッセージは「`never`型の値に`foo`というプロパティはないので`obj?.foo`はだめですよ:cry:」と主張しています。ここで突如登場した`never`型の意味が分からないとエラーの意味がよく分からないのではないでしょうか。

`never`型は「値が存在する可能性が無いことを表す型」です。`obj?.foo`は「`obj`が`null`や`undefined`でないときは`obj.foo`にアクセスする」という意味を持ちますが、では`obj`が`undefined`のときにそこから`null`型や`undefined`型の可能性を除外すると何が残るでしょうか。そう、何も残りませんね。この「何も可能性がない」状況を表してくれる概念が`never`型です。

要するに、「`obj`が`HasFoo | undefined`型のときは、`foo`プロパティへのアクセスが発生するのは`obj`が`HasFoo`型のときである」のと同様に、「`obj`が`undefined`型のときは、`foo`プロパティへのアクセスが発生するのは`obj`が`never`型のときである」という理屈です。

そして結局のところ、`never`型に対するプロパティアクセスは許可されません[^never_type]。これが型エラーの原因です。ここで言いたいことは、エラーメッセージに`never`型が出てきたら「絶対に走らない処理」を書いていることを疑うべきだということです。今回の場合は`obj?.foo`と書いても絶対に`foo`プロパティへのアクセスは発生しないのでした。

[^never_type]: `never`型のボトム型的な性質を考えると「`never`型に対しては任意のプロパティアクセスを許可してその結果も`never`型になる」というような推論も理論的には可能だと思いますが、ユーザーに対する利便性を考えて今の仕様になっているのだと思われます。

### Optional Chainingと型の絞り込み

TypeScriptのたいへん便利な機能のひとつは、条件分岐の構造を理解し自動的に型の絞り込みを行なってくれることです（type narrowing）。実は、Optional Chainingも型の絞り込みに対応しています。

※ この内容は記事執筆時点でまだTypeScript 3.7 betaに入っていませんが、[このプルリクエスト](https://github.com/microsoft/TypeScript/pull/33821)で実装されているためTypeScript 3.7に導入されることが期待されます。また、現在は実装されていませんがTypeScript 3.7のリリースまでには対応されそうなものもあります（[issue](https://github.com/microsoft/TypeScript/issues/34570)）。以下のサンプルはmasterブランチをビルドして動作を確認しました。

従来の型の絞り込みはこういう感じです。

```ts
function func(v: string | number) {
    if (typeof v === "string") {
        // ここではvはstring型
        console.log(v.length);
    }
}
```

この関数では`string | number`型の変数`v`に対して`typeof`演算子を使った条件分岐を書きました。TypeScriptはこれを検知し、if文の中では`v`を`string | number`型ではなく`string`型として扱います。`v`が数値である可能性を排除出来たことになりますね。これが型の絞り込みです。

では、Optional Chainingを混ぜてみましょう。

```ts
interface HasFoo {
    foo: number;
}

const obj: HasFoo | undefined = Math.random() < 0.5 ? { foo: 123 } : undefined;

if (typeof obj?.foo === "number") {
    // ここではobjがundefinedの可能性が消えているのでこれはOK
    console.log(obj.foo)
}
```

さっきと同様に`HasFoo | undefined`型を持つ変数`obj`に対して`typeof obj?.foo === "number"`というチェックを行っています。

実は、このチェックを通ったif文の中では`obj`が`undefined`である可能性が消えて`HasFoo`型となります。なぜなら、`obj`が`undefined`だった場合は`obj?.foo`は必ず`undefined`となり、`typeof obj?.foo === "number"`が満たされることはないからです。

他にも`if (obj?.foo === 123)`とか`if (obj?.foo)`のような判定でも同様に型の絞り込みが行われます。これはたいへん助かりますね。

このように、optional chainingを含んだ条件分岐を行うことでオブジェクトがnullishな値である可能性を消すことができます。

### 発展：Optional Chainの型推論の実装

これはTypeScriptコンパイラの内部処理に関する話なので、興味がない方は飛ばしても問題ありません。

Optional Chainingの型推論にあたっては、愚直に実装するとうまくいかない点があります。TypeScriptではその点をoptional typeと呼ばれる内部的な型の表現を導入することで乗り越えています。optional typeは「`?.`由来の`undefined`型」です。基本的には通常の`undefined`型と同じ振る舞いをする型であり、通常の`undefined`との違いはOptional Chainingの型推論の内部でのみ表れます。

Optional typeのはたらきを理解するために、次の例を見てみましょう。

```ts
interface HasFoo {
    foo: number;
}
interface HasFoo2 {
    foo?: number;
}

const obj: HasFoo | undefined = Math.random() < 0.5 ? { foo: 123 } : undefined;
const obj2: HasFoo2 | undefined = Math.random() < 0.5 ? { foo: 123 } : undefined;

// obj?.foo と obj2?.foo はどちらも number | undefined 型
const v1: number | undefined = obj?.foo;
const v2: number | undefined = obj2?.foo;

// これはOK
obj?.foo.toFixed(2)
// これは型エラー
obj2?.foo.toFixed(2)
```

`HasFoo`型と`HasFoo2`型はどちらも`foo`プロパティを持つオブジェクトですが、`foo`が`undefined`の可能性があるかどうかという違いがあります。

その違いは`obj?.foo`とか`obj2?.foo`では可視化されません。この2つはどちらも`number | undefined`型を持ちます。

しかし、`obj?.foo.toFixed(2)`と`obj2?.foo.toFixed(2)`のようにさらにチェーンを繋げるとその違いが表れ、前者はコンパイルが通る一方で後者は型エラーとなります。まずこの理由を理解しましょう。

まず前者を考えます。`obj`は`undefined`の場合と`HasFoo`の場合があり、前者の場合は`obj?.foo.toFixed(2)`は即座に`undefined`となり終了します。`obj`が`HasFoo`だった場合は、`obj?.foo`が`number`となり、よって`obj?.foo.toFixed(2)`の呼び出しは可能です。

次に`obj2`の場合を考えてみます。`obj2`が`undefined`の場合は先ほどと同様です。一方で`obj2`が`HasFoo2`の場合ですが、`HasFoo2`自体の`foo`が`undefined`の可能性を秘めているため`obj2?.foo`は依然として`number | undefined`型です。これにより、`obj2?.foo.toFixed(2)`は`obj2?.foo`が`undefined`の可能性があるため型エラーとなります。

ここで問題となるのは、`obj?.foo`と`obj2?.foo`の型だけを見るとどちらも`number | undefined`型となってしまい、それに対する`.toFixed(2)`呼び出しを許可していいのかどうか判断できないという点です。`obj?.foo.toFixed(2)`という一連の式を見た場合、コンパイラはまず`obj?.foo`部分の型推論を行い、その結果に対して`.toFixed(2)`の推論を行います。少なくとも今のTypeScriptの実装では、`(式).toFixed(2)`というノードの型推論を行うときに得られる`(式)`部分の情報はその型のみです。しかし、上で見たようにそれだけだと情報が不足しており適切な判断ができないというわけです。

この問題に対するワークアラウンドとして、コンパイラの内部でoptional typeが導入されました。これを便宜上`optional`と書くことにします（実際のTypeScriptプログラムでそう書けるわけではありません）。

具体的には、`?.`由来のundefinedに対しては`optional`型を付与します。そして、Optional Chain内部の型推論においては`optional`型を無視してプロパティアクセス等が可能かどうか判断します。

すなわち、`obj?.foo`の型推論結果は`number | optional`となります。これに対して`.toFixed(2)`のメソッド呼び出しの型推論を行うときは、一時的に`optional`を無視して`number`として扱います。そうするとメソッド呼び出しは許可され、結果は`string`となります。`optional`は伝播するので結果に`optional`を戻し、`obj?.foo.toFixed(2)`の型は`string | optional`となります。

一方、`HasFoo2`の場合は`foo`プロパティがもともと`number | undefined`でした。これにより、`obj2?.foo`の型は`number | undefined | optional`となります。これに対する`.toFixed(2)`呼び出しを考えると、`optional`を取り除いても依然として`undefined`型の可能性が残っているため型エラーとなります。

このようにして上記の2種類の式を区別しているのです。型の世界で話を終わらせるために内部的に特殊な型を導入するといいうことはTypeScriptのコンパイラでは結構行われています。

以上でTypeScriptの話は終わりです。

## Optional Chainingの歴史

ここからは、Optional Chainingの歴史を見ましょう。この概念がどれだけ昔からあったのか正確に知ることは難しいものの、確認できた限りで一番古いのはGroovyです。また、JavaScriptの文脈からするとCoffeeScriptの影響も大きいと考えられます。

### Groovy

JVM言語は苦手分野なのでよく分からないのですが、少なくとも2007年にはすでにGroovyは`?.`を持っていたという情報があります。

### CoffeeScript

CoffeeScriptのChangelogによれば、`?.`は2010年1月5日リリースのCoffeeScript 0.2.0で導入されました。Groovyをはじめとする他の既存の言語を受けたのかどうかはよく分かりませんが、`?.`という構文が一致していえることからその可能性は十分にありそうです。

CoffeeScriptはAltJSでありセマンティクスがJavaScriptと同じなので、CoffeeScriptが`?.`をどのように実装したのかは非常に興味深いところです。実際のところ、CoffeeScriptは現在のJavaScriptのOptional Chainingの挙動をほぼそのままの形ですでに実装していました。

ただ、先述のように構文はJavaScriptとは微妙に違っています。JavaScriptにおいて`?`という記号が条件演算子のせいで扱いにくいものになっていることを考えると、条件演算子を廃して`?`をフリーにしたのは英断と言えると感じられます。

ちなみに、CoffeeScriptは`?`はnullish関連のいろいろな場面で使われます。例えば`foo?`という式はJavaScriptの`foo != null`に相当します。また、JavaScriptで`foo ?? bar`と書く式もCoffeeScriptでは`foo ? bar`と書けます。

### 初期の議論

これをJavaScriptに入れたいという議論は2013〜2014年ごろからあったようです。メーリングリストでの議論が[esdiscuss.orgにまとまっています](https://esdiscuss.org/topic/existential-operator-null-propagation-operator)。他にも何個かスレッドがあり、プロポーザル文書からリンクが貼られています。

読むと分かりますが、初期からすでに`?.`という構文が優勢だったようです。`obj?[expr]`のようなものも模索されましたがやはり前述の理由でうまくいきません。他の構文の候補やセマンティクスなど一通りの議論がここでなされました。

### TC39ミーティング

その後舞台はTC39ミーティングへと移ります。TC39というのはJavaScript (ECMAScript) の仕様を策定する委員会で、仕様をJavaScriptに採用するかどうかはここで決められます。

採用候補の仕様はプロポーザルという形で管理されます。Optional Chainingもひとつのプロポーザルです。

プロポーザルはいくつかのステージに分類されます。ステージは0から4までの5段階あり、ステージ0は有象無象のアイデア、ステージ4は採用決定という段階です。現在Optional Chainingはステージ3です。ステージ3はほぼプロポーザルの内容が固まったので正式採用前だけど実装してみようぜという段階で、ここまで来ると我々が使えるようになり正式採用も近くなります。

ステージの上げ下げはTC39のミーティングによって決定されます。基本的にはステージが上がるかそのままかですが、稀にステージが下げられてしまうこともあります。

ここからは、ステージ上昇を賭けた各ミーティングの様子をざっくり振り返ります。

### Stage 1: 2017年1月

Optional ChainingがTC39ミーティングに最初に登場したのは2017年1月の回です。このプロポーザルは当初はNull Propagation Operatorという名前でした。

- [議事録](https://tc39.es/tc39-notes/2017-01_jan-24.html#13ic-seeking-stage-1-for-null-propagation-operator)

プロポーザルの最初の関門は、TC39の興味を惹きつけてStage 1に認定されることです。

議事録の初っ端に

> All: having a hard time reading the screen

と書いてあって笑いました。実際のスライドを見るとたしかに文字が小さいですね。

結論から言えばStage 1になることができたので終わりよければ全てよしですが。

Stage 1になるためには仕様の詳細まで決まっている必要はありません。実際、Stage 1になるためのスライドでは`?.`という演算子のコンセプトのみが述べられています。ただ、スライドとは別に今回の場合は初期から比較的詳細な案が用意されており、よい叩き台となったようです。

全体的に`?.`は好評でしたが、`?.[ ]`や`?.( )`は何か見た目が微妙だし本当に必要だろうかとか、短絡評価のセマンティクスが微妙とか、`( )`のあるなしで意味が変わってしまうのが微妙といった議論がありました。

また、ES2015で導入されたオプショナル引数が`undefined`のみに対応していることを考えると、`?.`が`null`と`undefined`に両対応すべきかそれとも`undefined`に対応すべきかも一考の余地がありそうでした。

とはいえ、これらの内容はStage 1になってから議論しても遅くはありません。ということで、無事にこのプロポーザルはStage 1になりました。今後Stage 2を目指すにあたってはこれらの内容が議論の焦点になります。


### 2017年7月

- [議事録](https://tc39.es/tc39-notes/2017-07_jul-27.html#13iia-optional-chaining-operator)

次にこのプロポーザルがTC39ミーティングに登場したのは7月です。前回のミーティングで挙げられた課題について回答をまとめ、Stage 2の承認を得ることが目的です。

ここで説明されたセマンティクスはおおよそ最終的なものと同じですが、`(a?.b).c`が`a?.b.c`と同じになる点が今と違いました。また、代入のサポートをするかどうかもまだ未定という段階でした。

このときのスライドはCoffeeScriptで書かれた既存のコードに対する利用状況調査などが含まれており面白いです。

結論としては、このミーティングでプロポーザルがStage 2に進むことはできませんでした。特に短絡評価周りで混乱が起こり、参加者を納得させられるより明確な説明が必要という結論になりました。次回の宿題ですね。

### 2018年1月

- [議事録](https://tc39.es/tc39-notes/2018-01_jan-24.html#13iiin-optional-chaining-update)

その2つ後のミーティングでOptional Chainingが再び議題にあがりました。これはStage 2が目標というよりはTC39に意見を聞きたいという意図のほうが強いようです。

今回、なぜか`?.`が`??.`に変わりました（GitHub上で投票を行ってみた結果のようです）。この場合`[ ]`や`( )`は`obj??[expr]`や`func??(arg)`となり少し見た目が良くなるので、そちら側に寄せた変更といえます。

そのほかは前回の課題であった短絡評価のセマンティクスの明確化がおもな議題です。多くのスライドや具体例を用いてセマンティクスが説明されました。また、分かりやすくする目的で仕様テキストも大きく書きなおされました。ちゃんと読んでいないのですが以前はNil Referenceというやばそうな概念があったらしく、改訂でそれが消されて現在のものに近くなりました（仕様書についてはあとで解説があります）。

TC39の反応としては`?.`でも`??.`でもどちらでも良さそうな感じでどちらかに決まることはありませんでした。短絡評価に関してはしっかりと説明したことで納得度が向上したようです。

### 2018年3月

- [議事録](https://tc39.es/tc39-notes/2018-03_mar-22.html#optional-chaining-for-stage-2)

TC39ミーティングは2〜3ヶ月に1回なので、前回に引き続いての登場です。前回比較的良い手応えを得たので今回はStage 2を目指しての議論となりました。なお、構文は`?.`に戻りました。もう一度投票してみたら`?.`が優勢になったことと、`??`にするとnullish coalescing operatorが`??:`になってしまうのが辛かったようです。

しかし、結論としては今回もStage 2に進むことができませんでした。`?.[ ]`や`?.( )`という構文に対する強烈な反対があり全然話が進まなかったようです。

### 2018年11月

- [議事録](https://tc39.es/tc39-notes/2018-11_nov-28.html#update-on-optional-chaining)

今回は発表者がこれまでと違うのでスライドの雰囲気が今までと全然違います。一見の価値があるかもしれません。

内容としては、まずオプショナルな関数呼び出し`func?.()`が消えてしまいました。`obj?.[expr]`は依然として残っています。`obj?.foo`も含めた三者がセットでなければいけないと主張していた勢力が折れた形です。

これに対する反応はまずまずといったところで、強い賛成も反対も見られない様子です。今回のミーティングでは話が右往左往してあまり進まなかった印象です。

### Stage 2: 2019年6月

- [議事録](http://rwaldron.github.io/tc39-notes/2019-06_june-5.html#optional-chaining-for-stage-2)

おまたせしました、約2年に渡った停滞を乗り越えてOptional ChainingがStage 2となったのが今年の6月のことです。つい最近ですね。ちなみに、今回はまた発表者が別の人です。

内容としては、`?.()`が復活しました。今回の議論の争点もそこだったらしく、これを入れるか入れないかがひとしきり議論されました。

反対意見もありましたが結局`?.()`を含んだままStage 2への移行が決定する形となりました。Optional Chainingに対するコミュニティの強い期待と、構文に関して長い時間をかけて解決策を模索したが何も見つからなかったことへの諦めから、やっとプロポーザルが次の段階に進むことができたという感じです。

ちなみに、Nullish Coalescing （`??`演算子）も同じミーティングでStage 2に移行しました。まあ、この2つはセットで扱われるのが自然ですからそんなに不思議ではありません。

### Stage 3: 2019年7月

- [議事録](http://rwaldron.github.io/tc39-notes/2019-07_july-25.html#optional-chaining-for-stage-3)

スケジュールの都合で2ヶ月連続となったTC39ミーティング（6月頭と7月終わりなので実際は1ヶ月半くらい間があります）です。前回Stage 2となったOptional Chainingはスピード出世でStage 3となりました。

今回のスライドでは、前回未だに争点となっていた`?.()`に関して重点的に説明されています。`?.()`のユースケースを集めてその必要性を説く形です。

そして今回の争点もやはり`?.()`でした。今回、`?.()`の見た目よりはその挙動が争点となったことで議論が白熱しました。`123?.()`のようなものもエラーではなく無視されるべきではないかというような話がされました。

色々と議論がありましたが結局周りに説得され、全会一致でStage 3への昇格が決まりました。めでたしめでたし。

### その後

プロポーザルがStage 3になると、実装が動き始めます。先述のようにTypeScriptがOptional Chainingの実装に向けて動き、3.7でリリースされます。

また、Webkitも[Stage 3への昇格直後に動き始め、8月のうちに対応を完了しています](https://bugs.webkit.org/show_bug.cgi?id=200199)。Chrome (V8) も同様に[8月のうちに対応しています](https://bugs.chromium.org/p/v8/issues/detail?id=9553)。Chromeは9月に公開された[Google Chrome 78 ベータ版](https://blog.chromium.org/2019/09/chrome-78-beta-new-houdini-api-native.html)にフラグ付きですがOptional Chainingのサポートが含まれています。

以上がOptional Chainingの歴史でした。Stage 4への昇格が楽しみですね。

## Optional Chainingの仕様

最後に、この節ではOptional Chainingを仕様の観点から見ていきます。やや難易度が高いので興味の無い方は飛ばしても大丈夫です（あと残っているのはまとめだけですが）。

なお、仕様書という場合は以下の文書を指しています。

- [ECMAScript 2019® Language Specification](https://www.ecma-international.org/ecma-262/10.0/index.html)
- [Optional Chaining](https://tc39.es/proposal-optional-chaining/)

### OptionalExpression

さて、仕様の観点から見ると、このプロポーザルは文法に[OptionalExpression](https://tc39.es/proposal-optional-chaining/#prod-OptionalExpression)という非終端記号を追加するものです。OptionalExpressionの定義を仕様から引用します（画像）。

![OptionalExpression](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233667/45500625-47cf-cf08-f8de-4e6800a65987.png)

読むと分かるように、OptionalExpressionは左側の部分（MemberExpression, CallExpression, OptionalExpressionのいずれか）にOptionalChainがくっついた形になっています。このOptionalChainという非終端記号が上で説明したオプショナルチェーンにちょうど対応します。

例えば`obj?.foo.bar`が文法上どう解釈されるかを表で表すと、このようになります。

<table>
  <tr>
    <td colspan="6"><b>OptionalExpression</b></td>
  </tr>
  <tr>
    <td rowspan="3"><b>MemberExpression</b><br>PrimaryExpression<br>IdentifierReference<br>Identifier<br>IdentifierName</td>
    <td colspan="5"><b>OptionalChain</b></td>
  </tr>
  <tr>
    <td colspan="2"><b>OptionalChain</b></td>
    <td rowspan="2"> </td>
    <td rowspan="2"><b>IdentifierName</b></td>
  </tr>
  <tr>
    <td> </td>
    <td><b>IdentifierName</b></td>
  </tr>
  <tr>
    <td><code>obj</code></td>
    <td><code>?.</code></td>
    <td><code>foo</code></td>
    <td><code>.</code></td>
    <td><code>bar</code></td>
  </tr>
</table>

ポイントは、先に説明したように`?.foo.bar`という部分全体がひとつのOptionalChainとして扱われていることです。OptionalChainの中身は左再帰で定義されており、先頭が`?.`であとは通常の`.`や`[ ]`、そしてArguments（これは関数呼び出しの`( )`を表しています）が続くことで構成されていることが分かります。

また、この文法から「チェーンの一部を括弧で囲むと意味が変わる」ということも読み取れます。括弧で囲まれた式は[ParenthesizedExpression](https://www.ecma-international.org/ecma-262/10.0/index.html#prod-ParenthesizedExpression)になりますが、これは[PrimaryExpression](https://www.ecma-international.org/ecma-262/10.0/index.html#prod-PrimaryExpression)の一種であり直接OptionalChainになることはできません。

例えば、`(obj?.foo).bar`の解釈は以下に定まります。

<table>
  <tr>
    <td colspan="7"><b>MemberExpression</b></td>
  </tr>
  <tr>
    <td colspan="5"><b>MemberExpression</b><br>PrimaryExpression<br>ParnthesizedExpression</td>
    <td> </td>
    <td><b>IdentifierName</b></td>
  </tr>
  <tr>
    <td rowspan="2"> </td>
    <td colspan="3"><b>Expression</b><br>（中略）<br>OptionalExpression</b></td>
    <td rowspan="2"> </td>
    <td rowspan="2"> </td>
    <td rowspan="2"> </td>
  </tr>
  <tr>
    <td><b>MemberExpression</b><br>（中略）<br>IdentifierName</td>
    <td colspan="2"><b>OptionalChain</b><br>（内訳は省略）</td>
  </tr>
  <tr>
    <td><code>(</code></td>
    <td><code>obj</code></td>
    <td><code>?.</code></td>
    <td><code>foo</code></td>
    <td><code>)</code></td>
    <td><code>.</code></td>
    <td><code>bar</code></td>
  </tr>
</table>

`obj?.foo.bar`と`(obj?.foo).bar`では構文木の形が大きく違うことが分かりますね。Optional Chainingの構文が前述のような木の形で定義される理由は、主に前述のセマンティクスを説明しやすいからです（逆に、構文上の表現に合致するようにセマンティクスを決めたとも言えます）。特に、括弧でオプショナルチェーンを切ることができるという構文上の事象とセマンティクスがちょうど合致していて扱いやすいものになっています。

### OptionalExpressionとタグ付きテンプレートリテラル

注意深い読者の方は、上で引用されたOptionalExpressionを見てあることに気づいたかもしれません。OptionalExpressionの定義にこのようなものが混ざっています。

![?. TemplateLiteral](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233667/1ee1c8a9-837c-d2ef-465e-51c658eef3c6.png)

これはすなわち、``func?.`abc` ``のような式がOptionalExpressionとして解釈されるということです。これば``func`abc` ``というタグ付きテンプレートリテラルのオプショナル版に見えます。

<table>
  <tr>
    <td colspan="5"><b>OptionalExpression</b></td>
  </tr>
  <tr>
    <td><b>MemberExpression</b></td>
    <td colspan="4"><b>OptionalChain</b></td>
  </tr>
  <tr>
    <td rowspan="2"> </td>
    <td rowspan="2"> </td>
    <td colspan="3"><b>TemplateLiteral</b><br>NoSubstitutionTemplate</td>
  </tr>
  <tr>
    <td> </td>
    <td><b>TemplateCharacters</b></td>
    <td> </td>
  </tr>
  <tr>
    <td><code>func</code></td>
    <td><code>?.</code></td>
    <td><code>`</code></td>
    <td><code>abc</code></td>
    <td><code>`</code></td>
  </tr>
</table>

しかし、先述の通り、**Optional Chainingはタグ付きテンプレートリテラルをサポートしていない**はずです。実際のところこう書いた場合はエラーが発生します。このことは、[仕様書の以下の場所](https://tc39.es/proposal-optional-chaining/#sec-left-hand-side-expressions-static-semantics-early-errors)に書かれています（画像で引用）。

![3.1.1 Static Semantics: Early Errors](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233667/cbe072bb-d69f-6f44-60bc-c796833e53f5.png)


要するに、**`` func.`abc` ``のような形は定義してあるけど実際には文法エラーになるよ**ということです。また、ここに書いてある通り`` obj?.func`abc` ``のような形も同様です。

わざわざ定義してから構文エラーにしなくても、最初から定義しなければいいと思われるかもしれません。そうしなかった理由は上記引用中にNOTEとして書かれています。これについて少し解説します。

理由を一言で説明すると、ここで明示的に定義しておかないと、**かの悪名高きセミコロン自動挿入によって別の解釈が可能になってしまう**からです。仕様書に書かれている例を引用します。

```js
a?.b
`c`
```

現在の定義では、これは2行でひとつの式として解釈されます。すなわち、``a?.b`c` ``というOptionalExpressionとして解釈され、上記の規則により構文エラーとなります。

一方で、これをOptionalExpressionとして扱う構文規則がなかった場合を考えてみます。まず、改行がない場合は`` a?.b`c` ``というコードは解釈することができずにやはり構文エラーとなります。タグ付きテンプレートリテラルは`MemberExpression TemplateLiteral`という並びによって定義されますが、上で見たようにOptionalExpressionはMemberExpressionの一種ではないからです。

これを踏まえて、上の2行のプログラムを見てみます。1行目の`a?.b`を読み終わって2行目に入り`` ` ``を読んだ時点で構文エラーが発生することになります。ここで[自動セミコロン挿入](https://tc39.es/ecma262/#sec-automatic-semicolon-insertion)が発動します。ここで適用されるルールをざっくり言うと「**改行の直後でコードが解釈不能な状況に陥ったら直前にセミコロンを挿入してみる**」というものです。これにより、上のプログラムの解釈はこれと同じになります。

```js
a?.b;
`c`
```

この解釈においては大まかに分けて2つの問題があります。ひとつは、**将来的な拡張が困難になる**点です。現在`` a?.b`c` ``がサポートされていないのはユースケースの欠如が理由とされています。つまり、関数が存在するときだけタグ付き関数呼び出しが出来るという機能の需要が無さそうなのです。もし将来的に需要が発見されたらこの機能をサポートする道もあるわけですが、上記のプログラムに別の解釈が与えられてしまうと将来的にその解釈を上書きすることができなくなってしまいます。それを避けるために、わざと文法エラーにすることで将来的な拡張の余地を残しているのです。

もうひとつの問題は通常の（オプショナルでない）タグ付きテンプレートリテラルとの対称性です。現在、以下のプログラムは`` a.b`c` ``として解釈されます。

```js
a.b
`c`
```

`a.b`を`a?.b`に変えるといきなり解釈が変わって文が2つになるというのはたいへん微妙だし非直感的ですね。思わぬミスが発生するのを避けるために`.`と`?.`の場合で解釈が大きく変わらないようになっています。

### OptionalExpressionとLeftHandSideExpression

OptionalExpressionの定義をもう一度振り返りましょう。

![OptionalExpression](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/233667/45500625-47cf-cf08-f8de-4e6800a65987.png)

一番下にすこし気になることが書いてあります。OptionalExpressionは、NewExpressionやCallExpressionと並んで、LeftHandSideExpressionの一種であるとされています。

LeftHandSideExpressionとは何の左側のことを指しているのでしょうか。実は、これは`=`の左側です。これが意味することは、`obj?.foo = 123`のような式が構文上可能であるということです。

<table>
  <tr>
    <td colspan="5"><b><a href="https://www.ecma-international.org/ecma-262/10.0/index.html#sec-assignment-operators">AssignmentExpression</a></b></td>
  </tr>
  <tr>
    <td colspan="3"><b>LeftHandSideExpression</b><br>OptionalExpression<br>（詳細は省略）</td>
    <td> </td>
    <td><b>AssignmentExpression</b><br>（中略）<br>IdentifierName</td>
  </tr>
  <tr>
    <td><code>obj</code></td>
    <td><code>?.</code></td>
    <td><code>foo</code></td>
    <td><code>=</code></td>
    <td><code>123</code></td>
  </tr>
</table>

やはり、これも構文上認められるとはいえ実際には文法エラー扱いになります。このことは仕様書の既存のEarly Error定義[12.15.1 Static Semantics: Early Errors](https://www.ecma-international.org/ecma-262/10.0/index.html#sec-assignment-operators-static-semantics-early-errors)に定義されています（該当部分を以下に引用）。

> It is an early Syntax Error if LeftHandSideExpression is neither an ObjectLiteral nor an ArrayLiteral and AssignmentTargetType of LeftHandSideExpression is strict.

実はプロポーザル文書のほうを見るとAssignmentTargetTypeではなくIsSimpleAssignmentTargetというものが定義されています。ちゃんと追っていないのですが、仕様執筆時にちょうどこのあたりの改稿が議論されていて齟齬があったようです。多分そのうち直るでしょう。

## まとめ

お疲れ様でした。この記事ではOptional Chainingの機能面を解説し、さらにStage 3に上がるまでの歴史と仕様の中身にも少し触れました。

機能面では短絡評価をちゃんと理解することがポイントとなります。一度理解すれば大したことはありませんのでぜひ今のうちに予習しておきましょう。

歴史に関しては、当初はセマンティクスで少し揉めたもののすぐに沈静化し、`?.( )`という構文が激しい反発を呼んで2年ものあいだ停滞したことがお分かりになったと思います。TC39の面々が年単位で考えぬいてベストだと判断された（というかは妥協して受け入れるまでに年単位の時間がかかった）`?.( )`構文ですから、素人考えで構文にああだこうだと文句を付けるのはあまり意味が無いことがお分かりでしょう。

仕様に関しては構文がどのように扱われているのかなどに興味がある方向けに解説しました。よく分からなくてもOptional Chainingの使用に問題はないと思いますのでご安心ください。

この記事執筆時にはStage 3プロポーザルであるOptional Chainingですが、多分ES2020か遅くともES2021くらいでStage 4に上がるものと思われます。楽しみですね。この記事を読んだみなさんはOptional Chainingに関して大抵のことは聞かれても答えられることでしょう。ぜひ周りにOptional Chainingを布教しましょう。

### 追記

- （2019年10月21日）調査不足により`?.`を持つ言語の中でCoffeeScriptが一番古いと思わせる書き方がされていましたが、実際のところGroovyのほうがさらに何年も前から`?.`を持っていました。お詫びして訂正します。