2025-04 の TC39 meeting
まとめ
決まったこと
Web compatibility issues / Needs Consensus PRs
Normative: add notation to PluralRules (ecma402#989, slides) | Ujjwal Sharma 複数形を扱う Intl.PluralRules に Intl.NumberFormat にもある "notation" オプションを追加する話。
ICU に入ったため、それが ECMAScript にも降りてきた形。
Normative: Mark sync module evaluation promise as handled (#3535, slides) | Nicolò Ribaudo (JSON や CSS のような)非 Cyclic Module Records において、モジュールの読み込みに失敗し Promise がリジェクトされたときに Unhandled Rejection を起こしてしまう仕様バグの修正。
仕様上同期モジュールであっても Promise を生成するが、それがリジェクトされたかどうかは無視するのが正しい。
Stage 4 (ES2026)
N/A
Stage 3
以前話されていた AsyncFromSyncIterator でイテレーターが閉じられた際に元の Iterator に伝播されていなかったのを修正する Normative Change。
今までは for-await-of でしか使われない仕様だったが Array.fromAsync にも使われることになることから再確認された。
Firefox Nightly に搭載された。
Temporal はマイクロ秒を扱う BigInt API を持っているが 75 bit で事足りるため binary64 のペアで実装するのがパフォーマンス的に良いという話がされている。
switch-case の中でスコープを作らずに使用できると予期せぬ動作を引き起こしてしまうということで、SyntaxError にするかどうかが議論され、承認された。
https://gyazo.com/5d11e3f6fce3de4b6e5a5e6a5de0492e
Stage 2.7
Test262 レビュー中。
Map#getOrInsert{,Computed} の提案。Stage 2.7 になった。
Stage 1 Stablilize Integrity で以前話されたが、Non-extensible Object に対して Private Fields を付与できる問題がある(The return override-mistake)。
code: js
class NonExtensibleBase {
constructor() {
Object.preventExtensions(this);
}
}
class ClassWithPrivateField extends NonExtensibleBase {
constructor(v) {
super();
this.#val = v;
}
}
new ClassWithPrivateField(42); // doesn't throw
これは PrivateFieldAdd と PrivateMethodOrAccessorAdd にバリデーションを追加することで解決することができる。
https://gyazo.com/9ab5ab8a6eb053fe420ff1f79f5bfa52
Chrome が調査したところ、この変更によって影響を受けるサイトは 0.000011% で Web 互換性的に大丈夫そう。
別の提案として Stage 2.7 になった。
Stage 2
Intl に Temporal の暦を入れる提案。ヒジュラ暦が太陰暦なため実装が大変らしい。
ブラウザのフィードバックで Mozilla から EventTarget で明示的に removeEventListener されていないハンドラーがあったときに、今までより多くのメモリリークを起こしてしまう懸念が示された。これを解決するためにイベントハンドラーではなく、イベント発火点から伝播するように変更された。
Mozilla は実装の労力に見合わないと懸念しており、Chrome はユースケースの価値を認めている状況。
export defer extracted from import defer: stage 2 update or for stage 1 (slides) | Nicolò Ribaudo export defer が import defer の提案からスプリットされ、Stage 2 になった。
Stage 1
即座にコードの実行を止める API の提案。トランザクションの整合性が重要なシステムに必要になるらしい。HostFaultHandler フックについては肯定的だが、Reflect.panic については否定的。
前回の会議で Decimal と Measure を統合する話があったが、そのまま分かれたまま進むこととなっていた。今回は新たに精度付き数値を扱うクラス(Decimal.Something/Amount)を追加したいという話が出ている。
https://gyazo.com/13d796cebcce42b3f93084080837798c
この新しいクラスが入ることになった場合 Intl.NumberFormat や Intl.PluralRules で受け入れるようになる。次回の会議で Stage 2 になるかもしれない。
文字列をコードポイントベースで比較するメソッドの提案。SQLite などで使うとのこと。
code: js
const arr = [
'\u{ff42}', // Fullwidth Latin Small Letter B
'\u{1d5ba}', // Mathematical Sans-Serif Small A
'\u{63}', // Latin Small Letter C
];
console.log('null locale compare', ...arr.sort(new Intl.Collator('zxx').compare)); // '𝖺', 'b', 'c' Stage 1 になった。
オブジェクトのプロパティ数を取得する API がないため、Object.keys(obj).length が広く使われているがこれはパフォーマンスがよくない。そこで Object.propertyCount を追加する提案。
enumerable や Symbol をカウントするかどうかを指定するオプションを入れるらしい(デフォルトは Object.keys と同じ)。
code: ts
type KeyType = "index" | "nonIndexString" | "symbol";
interface ObjectPropertyCountOptions {
keyTypes?: KeyType[] | undefined;
enumerable?: boolean | "all" | undefined;
}
Stage 1 になった。
Composites for stage 1 (repo, slides) | Ashley Claymore Records & Tuples の提案で新しいプリミティブを追加することが難しいと判明したため、Composites で新たにオブジェクトを追加する提案として進めていく。
code: js
const pos1 = Composite({ x: 1, y: 4 });
const pos2 = Composite({ x: 1, y: 4 });
Composite.equal(pos1, pos2); // true
const positions = new Set(); // the standard ES Set
positions.add(pos1);
positions.has(pos2); // true
Composite は凍結されるが、中に持つオブジェクトはそのままとなる。
code: js
const date = new Date();
const composite = new Composite({ date });
Object.isFrozen(composite); // true
composite.date === date; // true
比較をどうするか議論中。
Stage 1 となった。
Enums が TypeScript 互換なシンタックスで提案された。
code: ts
enum Numbers {
zero = 0,
one = 1,
two = 2,
three = 3,
alsoThree = three // self reference
}
今のところ TypeScript と違って値が自動で入らないようになっている(auto を使った別のシンタックスで入れることになるかもしれない)。Stage 1 になった。
AsyncContext.Variable の提案は現状 run メソッドでスコープを作る必要がある。これにより函数を新たに AsyncContext でラップする場合に変更するコードが多くなってしまう。
code: js
function* gen() {
yield 1;
yield 2;
}
// ↓
const asyncVar = new AsyncContext.Variable();
function* gen() {
const span = createSpan();
yield* asyncVar.run(span, function *() {
yield 1;
yield 2;
});
}
そこで Symbol.dispose メソッドを付与し、using を使って簡単に追加できるようにする提案。
code: js
const asyncVar = new AsyncContext.Variable();
function* gen() {
using _ = asyncVar.withValue(createSpan());
yield 1;
yield 2;
}
Stage 1 になった。
Stage 0
N/A
Withdrown
Stage 1 Composites を進めることとなり廃止。
その他
WebKit が Standards Positions で、TC39 会議でフィードバックを受けるよう要請していたため議題に出た。
Observable については Zenn に記事を書いている。
Guidelines for Locale-Sensitive Testing in Test262 (slides) | Philip Chimento Test262 に Locale-Sensitive なテストが入ると困る話。
Update to Consensus policy (slides) | Michael Saboff TC39 の合意形成プロセスについて。単独反対者が提案をブロックするにはもうひとり別会社の支持者を必要とするなどの案が出たが保留。
総括
Records & Tuples が廃止され、新たに Stage 1 Composites に置き換わった。
今回は Stage 1 が多く、特に TypeScript から Enum を ECMAScript に入れようとする流れは興味深く思う。