Closed10

`for ((async) of []) {}` みたいなやつ

Sosuke SuzukiSosuke Suzuki

JavaScript で for (async of []) {} みたいな形をしたものについての仕様の調査。

どういう場合に for (n of []) {}n にカッコをつけないといけないのかわからない

Sosuke SuzukiSosuke Suzuki

V8(chrome)では

for (async of []) {}

は妥当。しかし、Babel はそれをパースできず、async にカッコをつける必要がある。

また、

for (let of []) {}

は V8 でも Babel でもエラーになるので、仕様としては、どういうときにカッコをつける必要があるんだろうねっていうのを調査する。

Sosuke SuzukiSosuke Suzuki

あ、なんで調査する必要があるかっていうと Prettier でどういうときにカッコをつけるといいんだろうっていうことです

Sosuke SuzukiSosuke Suzuki

for-in, for-of, fo-await-of のそれぞれに3つずつ規則がある

簡単にいえば、それぞれに対して

for (variable in expression) {}

for (var variable in expression) {}

for (let variable in expression) {}
for (const variable in expression) {}

風のものが定義されている。で、今回関係あるのは最初のやつ。

uhyouhyo

for-in, for-of, for-await-ofで事情が異なります。

for-in について

for (n in expression) {} の形に絞ると、構文定義が次のようになっています。

for ( [lookahead ≠ let [ ] LeftHandSideExpression in Expression) Statement

LeftHandSideExpressionは、予約語ではない識別子は全て許します。したがって、以下のようなものは全てOKです(括弧で囲む必要がない)。

for (let in {}) {}
for (async in {}) {}
for (await in {}) {} // 普通の関数の中ならOK

一方、for (const in {}) はだめです(constは予約語であるため)。constはもともと識別子ではないので、括弧で囲んでも意味はありません。

“lookahead ≠ let [ ” というのは、 その位置に続くトークン列が let [となるものは禁止されている(この生成規則に当てはまらない)ということです。

// OK
for (foo["a"] in {}) {}
// だめ
for (let["a"] in {}) {}
// OK
for ((let["a"]) in {}) {}

以上から、括弧で囲む意味があるのは let[ で始まる式が in の左に来るときだけです。

for-ofについて

for ( [lookahead ∉ { let, async of }] LeftHandSideExpression of AssignmentExpression) Statement

今後は ”[lookahead ∉ { let, async of }]”とされています。よって、for (の直後に let が来る場合、この生成規則には当てはまらないとみなされ、すなわち let は識別子の let として解釈されることはありません。 letが識別子ではないということは後ろに変数名かパターンが必須であり、結果として次のものは構文エラーとなります。

// だめ
for (let of []) {}

この場合はlet(let)とすることで構文エラーを回避できます。これは、for (の直後のトークン列がletではなく( letとなり制限を回避できるからです。

// OK
for ((let) of []) {}

Chromeでは Unexpected token '[' というSyntaxErrorが出ますが、これがletが変数名ではなく変数宣言の先頭のletであると解釈された証拠です。

先のlookaheadではasync ofも禁止されています。よって、次のものは上の構文規則に当てはまりません(for (の後ろにasync ofと続いているため)。

// だめ
for (async of []) {}

これも同様にasync(async)にすれば回避できます(for (の直後のトークン列がasync ofではなく( async ) of となるため。

for-await-of について

for await ( [lookahead ≠ let] LeftHandSideExpression of AssignmentExpression) Statement

これに関しては [lookahead ≠ let] のみなので、 for await (async of []) {} はOKです。

結論

for (n in expression) {}
  • どんな変数に対しても括弧は必要ありません。
for (n of expression) {}
  • nletまたはasyncのときに括弧が必要です。
for await (n of expression) {}
  • nletのときのみ括弧が必要です。

nが識別子だけでなく一般の式に広げると、for-inの場合は「let[で始まる式全般」に括弧が必要であり、for-ofについては「letで始まる式全般」および「async ofで始まる式全般」(アロー関数になるので括弧で囲んでも構文エラーになりますが)に括弧が必要です。for-await-ofの場合は「letで始まる式全般」に括弧が必要です。

Sosuke SuzukiSosuke Suzuki

で、次は Prettier が使っている各パーサーがどのように対応しているのか

  • babel
  • espree
  • meriyah
  • typescript-estree
  • flow
  • babel-ts
  • babel-flow

多いね

このスクラップは2021/05/24にクローズされました