`for ((async) of []) {}` みたいなやつ
JavaScript で for (async of []) {}
みたいな形をしたものについての仕様の調査。
どういう場合に for (n of []) {}
の n
にカッコをつけないといけないのかわからない
V8(chrome)では
for (async of []) {}
は妥当。しかし、Babel はそれをパースできず、async にカッコをつける必要がある。
また、
for (let of []) {}
は V8 でも Babel でもエラーになるので、仕様としては、どういうときにカッコをつける必要があるんだろうねっていうのを調査する。
あ、なんで調査する必要があるかっていうと Prettier でどういうときにカッコをつけるといいんだろうっていうことです
for-in, for-of, for-await-of の仕様 https://tc39.es/ecma262/#sec-for-in-and-for-of-statements
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) {}
風のものが定義されている。で、今回関係あるのは最初のやつ。
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) {}
-
n
がlet
またはasync
のときに括弧が必要です。
for await (n of expression) {}
-
n
がlet
のときのみ括弧が必要です。
n
が識別子だけでなく一般の式に広げると、for-inの場合は「let[
で始まる式全般」に括弧が必要であり、for-ofについては「let
で始まる式全般」および「async of
で始まる式全般」(アロー関数になるので括弧で囲んでも構文エラーになりますが)に括弧が必要です。for-await-ofの場合は「let
で始まる式全般」に括弧が必要です。
ありがとうございます!
で、次は Prettier が使っている各パーサーがどのように対応しているのか
- babel
- espree
- meriyah
- typescript-estree
- flow
- babel-ts
- babel-flow
多いね
実装コストに対して何もうれしくないので一旦やめました。