1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

【TypeScript/JavaScript】Optional Chainingのトランスパイル後がわからなかったので調べてみた

Posted at

こちらの記事で、TypeScript
v3.7からの新機能「Optional Chaining」が解説されていました。
その中に「Optional Chaining」を使用したコードのトランスパイル結果が記載されています。

例えばこんなコードがあるとして

interface Foo {
    name: string
    bar: {
      name: string
      baz?: {
        name: string
      }
    }
  }

  let foo: Foo

  foo = { name: 'a', bar: { name: 'bar' } }
console.log(foo.bar.baz?.name)

トランスパイルするとどうなるかというと下記のようになります。

var _a;
let foo;
foo = { name: 'a', bar: { name: 'bar' } };
console.log((_a = foo.bar.baz) === null || _a === void 0 ? void 0 : _a.name);

このトランスパイル後のソース、特に4行目のコード

console.log((_a = foo.bar.baz) === null || _a === void 0 ? void 0 : _a.name);

がどのような文法で成り立っているかわからなかったので、調査してみました。

※: 記事内に間違っている箇所があればコメントで教えてくださるとたすかります。

前提条件

以下の文法については理解している前提とします。

  • =====の違い
  • 三項演算子

ポイント

  • 代入式の返り値は「代入した値」
  • void 0undefinedと等価

代入式の返り値は「代入した値」

まず前半部分について注目してみます。

(_a = foo.bar.baz) === null

JavaScriptでは、代入式の返り値は「代入した値」になります。つまり、(_a = foo.bar.baz)の返り値はfoo.bar.bazです。
また、interface Fooの定義により 、foo.bar.bazの型はTypeScript上ではstring|undefinedであるため、
左辺(_a = foo.bar.baz)の返り値は何らかの文字列もしくはundefinedです。
その返り値の値をnullと厳密比較している、というのが上式の意味です。

ここで、===による厳密比較がtrueになるには両辺の型が一致している必要がありますが、
string|undefinednullでは型がそれぞれ異なるためこの式の結果は必ずfalseになります。

つまりこの式は、_afoo.bar.bazを代入した上で、||以降の式を必ず実行する仕掛けになっています。

void 0undefinedと等価

次に後半部分です。

_a === void 0 ? void 0 : _a.name

void 0という見慣れない文法がありますが、これはundefinedと等価です。
上式をundefinedで置き換えるとわかりやすくなります。

_a === undefined ? undefined : _a.name

_aがundefinedならundefined、そうでないなら_a.nameを返す式ですね。
undefinedでなくvoid 0を使用するのは、「undefinedが書き換え可能である」というのが理由の一つとしてあると思います。

参考: void(0)とundefinedの使い分け

なぜundefinedの代わりに使うのか?

jsではundefinedは値であり、すなわち上書きができる。そのため代わりとして、voidが使われる。

example
console.log(a === undefined) // true
console.log(a === void 0)    // true

// undefinedをうわがく
var undefined = 100

console.log(a === undefined) // false
console.log(a === void 0)    // true

void 0を使用することでundefinedの書き換えがトランスパイルに影響されないようになっています。

まとめ

以上より、トランスパイル後のコードは「_afoo.bar.bazを代入し、_aundefinedでなければ_aのプロパティnameを返す。そうでなければundefinedを返す」という挙動であることがわかりました。

(※ でも、foo.bar.bazを直接比較せずに_aに代入してから比較するのはなんでだろう。。。。分かる人いたら教えてください。)

1
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?