Ecmascript proposal report 2021春

6 min read読了の目安(約6200字

Ecmascriptの現在議論中の仕様について、執筆時点で自分が気になってるものをまとめてみました。

間違いなどありましたら、お気軽にご連絡いただければと思います。未熟者ゆえご容赦ください。。。

Ecmascriptの仕様策定

そもそもEcmascriptってどうやって仕様決めてるのか?というところなんですが、Ecmascriptの仕様はTC39 という組織(さらにいえば、TC39はecma internationalという組織のうちの1つ)によって管理されています。

Ecmascriptの新しい仕様は、TC39のproposals(提案)リポジトリで管理されており、以下のStageという段階に分けられます。

stage overview detail
stage0 Strawperson アイディア段階、やりたきことを議論
stage1 Proposal 提案ドキュメントとしてまとまってる状態
stage2 Draft 仕様書の初期ドキュメントがまとまってる状態
stage3 Candidate ブラウザの実装待ち
stage4 Finished 仕様策定完了

ここからは僕が気になってるProposalを独断と偏見でまとめていきます。
※Propsalの一覧はこちら から。

Import Assertions(Staeg3)

https://github.com/tc39/proposal-import-assertions
https://github.com/tc39/proposal-json-modules

syntax

// importの最後に`assert { type: "json" }`を追加
import json from "./foo.json" assert { type: "json" };
import("foo.json", { assert: { type: "json" } });

概要

いわゆるCSS Modulesなどのように、JS以外も標準でimportできるようにしていきたいよねというSynthetic Modulesと呼ばれる機能実装の一貫で出てきたファイル検証の構文とJSONのimport。
importする際にMIMEだけで拡張子判断することは、サーバー側が予期せず異なるMIMEを提供した場合、管理者やユーザーにとって予期せぬコードの実行が行われる可能性もあるのでセキリティ的によくないということになり、その結果、MIME+利用者側でimportするファイルがなんなのか判断する必要ありとなり、このimport assertionが提案されました。

拡張子で判断できるのでは?と思ったんですが、Web上においてリクエストURLの末尾拡張子=解釈方法ではない、というこれまでの原則に則ると成り立たない(ex: .cssって拡張子がリクエストに入ってなくても実際にはCSSが帰ってくるかもしれない)とのことでした。ついwebpack前提で考えてしまいますが、ブラウザからimportされることを考えると納得ですね。

今後の影響

このPropsalにより、今後JS以外の拡張子のimportが標準仕様となっていく動きが強まっていくと思われます。これは既存のCSS modulesなどと微妙に仕様が異なるとか、css-loaderとbabelどっちでどういう責務分けをするのかとかの議論につながっていくと思われ、トランスパイラ・コンパイラを利用している我々にも何かしらの影響が出てくるかもしれません。

Record&Tuple(Stage2)

https://github.com/tc39/proposal-record-tuple

syntax

// Objectや配列の宣言に`#`を足して`#{}`, `#[]`で宣言
const proposal = #{
  id: 1234,
  title: "Record & Tuple proposal",
  contents: `...`,
  // tuples are primitive types so you can put them in records:
  keywords: #["ecma", "tc39", "proposal", "record", "tuple"],
};

const measures = #[42, 12, 67, "measure error: foo happened"];

概要

immutableJS に代表されるようなImmutableなデータ構造を標準としてサポートしようというProposalです。
通常のObjectや配列とほぼ同じですが、以下2つの大きな特徴を持ってます。

  • deeply immutable(Mutableな要素は含められない)
  • 比較が参照ではなくなる(下記参考)
assert(#{ a: 1 } === #{ a: 1 }); // true
assert(#[1, 2] === #[1, 2]); // ture

ただし、immutableJSのように.set()/.get()のようなメソッドは持っていません。

今後の影響

TypescriptでDeepReadonly<T>とか作って書いてた自分としては普通に嬉しいし、
immutablityと同時に参照を気にしなきゃいけないReduxないしReactとかは結構恩恵あるかもって思ったりしてます。
エコシステム目線で言うと、これをTypescriptがサポートするのはそこまで難しくない気がします(readonlyあるし)が、babelやprettierがどこまでやるのか次第ではちょっと大変そうにも思えます。
代入しようとしてもSyntaxErrorではなく、TypeErrorっぽいからそこまで難しくないのかな・・・

Temporal(Stage2)

https://github.com/tc39/proposal-temporal

syntax

Temporal.now.instant()
// 2021-01-13T20:57:01.500944804Z

const date = Temporal.PlainDate.from({year: 2006, month: 8, day: 24}); // => 2006-08-24
date.year // => 2006
date.inLeapYear // => false
date.toString() // => "2006-08-24"

概要

JSでは日時を扱うDateObjectがややこしく忌み嫌われてきて、momentdayjsdate-fnsなどの日時を扱いやすくするためのライブラリがいくつか登場してきました。
こういった状況を標準としても打破すべきとして登場したのがTemporalです。なぜこんなややこしい名前なんだろうと個人的には思いますが、まぁ確かにDateって命名は使われてるし、日付を扱うとはいえ前回のものとは全く異なるAPIなので命名難しいところですね。
TemporalはこれまでのDateと異なり、以下のような特徴を持っています。

  • immutable
  • タイムゾーンを意識しなくて済むPlainな扱いやdurationなどの扱いが可能
  • nowとして取得できるのがシステムの完全にタイムゾーン由来

今後の影響

その他細かいメソッドや内包Objectについては省略しましたが、Temporalの登場によってJSの日時計算処理系にライブラリの導入が実質必須みたいな状態から抜けられるんじゃないでしょうか。
※その他の詳細なAPIの仕様はこちらを参照ください。

do expressions(Stage1)

https://github.com/tc39/proposal-do-expressions
https://github.com/tc39/proposal-async-do-expressions

syntax

// do expression
let x = do {
  let tmp = f();
  tmp * tmp + 1 // 最後の評価がxに代入される
};

// in jsx
return (
  <nav>
    <Home />
    {
      do {
        if (loggedIn) {
          <LogoutButton />
        } else {
          <LoginButton />
        }
      }
    }
  </nav>
)

// async do
async do {
  await readFile('in.txt');
  let query = await ask('???');
  // etc
}

概要

ScalaやRustのように、ブロックの最後に評価した値を暗黙的に返します。
個人的にはJSにif式がないのは手続き型の助長にしかならないと思ってたので嬉しくはあるんですが、ちょっと冗長(特にasync doとかになるとちょっとややこしい)な気もしてて、結局欲しかったのってif式なのではって気もします。
とかいいつつ、実際出てきたらjsxで多用しそうですね。

Pipeline Operator(Stage1)

https://github.com/tc39/proposal-pipeline-operator

syntax

let person = { score: 25 };

let newScore = person.score
  |> double
  |> (_ => add(7, _))
  |> (_ => boundScore(0, 100, _));

概要

F#やElmで馴染みのあるパイプラインオペレータです。現在、Syntaxについて絶賛議論中のとなってますが、初期提案から3年ほど立っており、実際stage2にいくのかこのままマージされず棄却されるのか不明ですが個人的には楽しみにしてるProposalなので期待も込めて挙げてみました。
どうやら問題はasyncをどう扱うかで、特殊なawaitを仕様にするかawait時だけシンタックスを変えるかなどいろいろ議論されているようです。
async抜きなら、似たようなことはlodash/flowで十分実装できるので、本当に標準サポートする必要があるかまでは正直懐疑的ですが、個人的な趣味嗜好としてはとても欲しいです。

Decorator(Stage2)

https://github.com/tc39/proposal-decorators

syntax

// exmaple1
@defineElement("my-class")
class C extends HTMLElement {
  @reactive prop clicked = false;
}

// exmaple2
function logged(value, { kind, name }) {
  if (kind === "method") {
    return function (...args) {
      console.log(`starting ${name} with arguments ${args.join(", ")}`);
      const ret = value.call(this, ...args);
      console.log(`ending ${name}`);
      return ret;
    };
  }
}

class C {
  @logged
  m(arg) {}
}

new C().m(1);
// starting m with arguments 1
// ending m

概要

Typescriptなどに古くからあるDecoratorです。DecoratorのProposalは歴史が古く、5年以上議論されてきています。PHPでもattributeが採用され、Rustにもマクロという似たような機構があったりでこの辺は昨今の流行りな感もあります。
機能としてはまぁ普通にClassのデコレータですね(雑)。

今後の影響

TypescriptだとDecoratorはexperimentalだったと思うので、標準採用されれば利用者も増えそうですね。
Web ComponentsのcustomElements.defineとか個人的には面倒なんでこれでできると嬉しかったりします。

まとめ

まだまだ気になってるProposalはあるんですが、以外と調べてまとめるのが時間かかって大変だったので今回はこの辺までにしようと思います。
普段はJxckさんのmozaic.fmでこの辺の話を聞いてるんですが、やっぱり自分で調べ直すとかなり大変ですね、、、
近々やる気が維持できたら残りの気になってるProposalまとめるか、もしくはCSSの仕様策定に明るくないので、そっちをがんばってみようかと思います。

この記事に贈られたバッジ