Contents
- 1 ES2025 の概要と主要提案
- 2 主要提案の仕様詳細と API
- 2.1 Iterator Helpers — 概要
- 2.2 Iterator Helpers — API サマリ
- 2.3 Iterator Helpers — 例外伝播と評価タイミング
- 2.4 Iterator Helpers — 同期/非同期の違いとコード例
- 2.5 Iterator Helpers — TypeScript 型定義例(推奨)
- 2.6 Iterator Helpers — 実装・ポリフィル情報(一次ソース)
- 2.7 Promise.try — 概要
- 2.8 Promise.try — 仕様(正式名と挙動)
- 2.9 Promise.try — TypeScript 型拡張(宣言マージ)
- 2.10 Promise.try — 実装状況と出典
- 2.11 Import Assertions / JSON Modules — 概要
- 2.12 Import Assertions — 静的 / 動的の違いとコード例
- 2.13 Import Assertions — Node.js 実装とランタイム要件
- 2.14 Import Assertions — バンドラ挙動差と設定例
- 2.15 Import Assertions — フォールバック/代替
- 3 ブラウザ/Node の実装状況と互換性
- 4 トランスパイル/ポリフィル戦略と TypeScript 対応
- 5 既存コードからの移行ガイド(具体例と手順)
- 6 CI・ベンチマーク・検出スニペット(実務向け)
- 7 参考(一次ソース・ドキュメント)
- 8 まとめ
ES2025 の概要と主要提案
この節では ES2025 に含まれる代表的な提案を概観します。各機能は言語の利便性を高めますが、実装差やビルド時の扱いの違いに注意が必要です。
代表的な提案
主に実務で影響が大きい項目を列挙します。個別の詳細と一次ソースは後述します。
- Iterator Helpers / Async Iterator Helpers:イテレータに map/filter/take/toArray 等を追加し、遅延評価チェーンを可能にします。
- Promise.try:同期例外を捕捉して Promise を返すユーティリティ(Bluebird 由来)。
- Import Assertions / JSON Modules:import のアサーションを通じて JSON をネイティブにモジュールとして読み込めるようにします。
主要提案の仕様詳細と API
この節では、各提案を厳密に分解します。API シグネチャ、戻り値の型、同期/非同期差、例外伝播のタイミング、TypeScript 用の型定義例、一次ソースへのリンクを提示します。
Iterator Helpers — 概要
Iterator Helpers は言語側でイテレータ(同期/非同期)に対する便利メソッドを提供します。これにより外部ライブラリに頼らず遅延評価チェーンが記述できます。
Iterator Helpers — API サマリ
API の代表的なシグネチャを示します。以下は仕様の要点です(参照: TC39 提案)。
- 同期イテレータ(%IteratorPrototype% に追加)
- map(callback: (value: T, index: number) => U): Iterator
- filter(predicate: (value: T, index: number) => boolean): Iterator
- take(n: number): Iterator
- drop(n: number): Iterator
- toArray(): T[] — イテレータを評価して配列を返します(同期版)。
- 非同期イテレータ(%AsyncIteratorPrototype% に追加)
- map(callback: (value: T, index: number) => U | PromiseLike): AsyncIterator
- filter(predicate: (value: T, index: number) => boolean | PromiseLike
): AsyncIterator - take(n: number): AsyncIterator
- toArray(): Promise
— 非同期イテレータは Promise を返します。
一次ソース(仕様・提案):
- TC39 proposal: https://github.com/tc39/proposal-iterator-helpers
(MDN の対応ページや各ランタイムの実装状況は下段の互換性節にまとめます。)
Iterator Helpers — 例外伝播と評価タイミング
各メソッドは遅延評価です。map/filter のコールバック内で例外が発生した場合、その例外は消費側が next() を呼んだ時点で伝播します。toArray は強制評価を行うため、呼び出し時点で例外がスローまたは Promise が拒否されます。
- 同期例: コールバック内の throw は next() を呼ぶ瞬間に伝播します。
- 非同期例: コールバックが Promise を返してそれが拒否されると、next()(非同期の場合は拒否された Promise)が返されます。toArray() は Promise を返し、拒否はそこに伝播します。
Iterator Helpers — 同期/非同期の違いとコード例
各 H3 の導入文として、同期版は即時 toArray() が配列を返し、非同期版は Promise
- 同期(例)
|
1 2 3 4 |
const it = [1, 2, 3].values(); const doubled = it.map(x => x * 2).filter(x => x > 2).toArray(); // doubled -> [4, 6] |
- 非同期(正しい await の使い方)
|
1 2 3 4 5 6 7 8 |
async function* gen() { yield 1; yield 2; yield 3; } const ai = gen(); const mapped = ai.map(async x => x * 2); // AsyncIterator const results = await mapped.toArray(); // Promise<T[]> console.log(results); // [2,4,6] |
注意: 非同期イテレータの toArray は Promise を返します。await を忘れると配列ではなく Promise オブジェクトになります。
Iterator Helpers — TypeScript 型定義例(推奨)
TypeScript ではイテレータ側の型名が環境によって異なるため、一般的には IterableIterator/AsyncIterableIterator を拡張するのが現実的です。下記は宣言ファイル例です。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// types/iterator-helpers.d.ts export {}; declare global { interface IterableIterator<T> { map<U>(fn: (value: T, index: number) => U): IterableIterator<U>; filter(fn: (value: T, index: number) => boolean): IterableIterator<T>; take(n: number): IterableIterator<T>; toArray(): T[]; } interface AsyncIterableIterator<T> { map<U>(fn: (value: T, index: number) => U | PromiseLike<U>): AsyncIterableIterator<U>; filter(fn: (value: T, index: number) => boolean | PromiseLike<boolean>): AsyncIterableIterator<T>; take(n: number): AsyncIterableIterator<T>; toArray(): Promise<T[]>; } } |
この定義は暫定的対応です。公式 lib 更新を待つか、プロジェクト内で型ファイルを配布してください。
Iterator Helpers — 実装・ポリフィル情報(一次ソース)
- TC39 提案(仕様): https://github.com/tc39/proposal-iterator-helpers
- core-js(汎用ポリフィル): https://github.com/zloirock/core-js — (注)core-js の各リリースで Iterator Helpers のサポート状況は変わるため changelog を確認してください。
- community polyfills / ライブラリ: npm で "iterator helpers" を検索し、単体パッケージ(非公式)が存在する場合があります。機能検出を組み合わせて選定してください。
Promise.try — 概要
Promise.try は、同期的に例外を投げる可能性のある関数を安全に Promise 化するための静的メソッドです。Bluebird の Promise.try に由来します。
Promise.try — 仕様(正式名と挙動)
正式名は Promise.try(静的メソッド)として提案されています。振る舞いは簡潔に言えば次の通りです。
- Promise.try(fn) は内部で Promise.resolve().then(() => fn()) と等価です。
- fn が同期的に throw すると、Promise は拒否されます。
- fn が thenable を返した場合、その thenable の解決・拒否に従います。
- fn 内で非同期にエラーが発生すれば、その Promise は拒否されます。
一次ソース(提案):
- TC39 proposal: https://github.com/tc39/proposal-promise-try
Promise.try — TypeScript 型拡張(宣言マージ)
PromiseConstructor を拡張する正しい宣言マージ例を示します。プロジェクトに types/promise-try.d.ts を置いてください。
|
1 2 3 4 5 6 7 8 |
// types/promise-try.d.ts export {}; declare global { interface PromiseConstructor { try<T>(fn: () => T | PromiseLike<T>): Promise<T>; } } |
ランタイムポリフィル(簡易)
|
1 2 3 4 5 6 |
if (typeof Promise.try !== 'function') { Promise.try = function (fn) { return Promise.resolve().then(fn); }; } |
Promise.try — 実装状況と出典
- TC39 提案: https://github.com/tc39/proposal-promise-try
- MDN(Promise のドキュメント): https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise
注意: Promise.try は全ランタイムで必ずしもサポートされていないため、ポリフィル導入や代替パターン(Promise.resolve().then(() => fn()) や async/await + try/catch)を検討してください。
Import Assertions / JSON Modules — 概要
import assertions により import 文にアサーションを付与し、assert { type: 'json' } で JSON をモジュールとして読み込めます。静的解析やバンドラ/サーバ側の扱いが重要です。
Import Assertions — 静的 / 動的の違いとコード例
静的インポート(ビルド時/ロード時に処理される)
|
1 2 |
import cfg from './config.json' assert { type: 'json' }; |
動的インポート(ランタイムでの読み込み)
|
1 2 3 |
const mod = await import('./config.json', { assert: { type: 'json' } }); const cfg = mod.default; |
動的インポートでもアサーションオブジェクトを渡す必要があります。静的インポートはモジュールグラフの一部として扱われ、モジュールキャッシュの影響を受けます。
Import Assertions — Node.js 実装とランタイム要件
Node では ESM(.mjs または package.json に "type":"module")での利用が前提です。Node の JSON モジュールサポートは段階的に導入され、古いバージョンではフラグが必要でした。詳細は Node の公式ドキュメントを参照してください。
一次ソース(Node の ESM ドキュメント):
- Node.js ESM: JSON Modules: https://nodejs.org/api/esm.html#json-modules
Import Assertions — バンドラ挙動差と設定例
バンドラにより挙動が異なります。次は実務で使える設定例と注意点です。
- Webpack(5 系)
- 必要: experiments.importAssertions を有効化
-
例:
js
// webpack.config.js
module.exports = {
experiments: { importAssertions: true },
module: {
rules: [{ test: /\.json$/, type: 'json' }]
}
}; -
参照: https://webpack.js.org/configuration/experiments/
-
Rollup
- 標準では静的 JSON のインライン取り込みに @rollup/plugin-json を使う
-
例:
js
// rollup.config.js
import json from '@rollup/plugin-json';
export default { plugins: [json()] }; -
参照: https://github.com/rollup/plugins/tree/master/packages/json
-
Vite
- Vite は JSON をネイティブにサポートしますが、assertion 構文の扱いはバージョン差があるためドキュメントを確認してください。
-
参照: https://vitejs.dev/guide/features.html#json
-
esbuild / SWC
- 高速トランスパイラはランタイムポリフィルや挙動変換を自動で行いません。必要なら loader 設定やプラグインで変換します。
- esbuild: https://esbuild.github.io/api/#loader
バンドラ差の要点:
- 一部のバンドラはアサーションを無視して標準の JSON インポートとして扱います。アサーションをそのまま保持して実行環境に渡すことが重要な場合は、バンドラの対応と設定を必ず確認してください。
Import Assertions — フォールバック/代替
ランタイムやバンドラで未サポートの場合はフォールバックを用意します。代表的な手法:
- 静的ビルド時に JSON を JS モジュールに変換する(ビルドプラグインで default export に変換)。
- 実行時フォールバック: fetch + response.json()(ブラウザ)や fs.readFile + JSON.parse(Node)を使う。
ブラウザ/Node の実装状況と互換性
この節では実装状況の確認方法と注意点を示します。ランタイムのリリースノートや MDN のブラウザ互換表を必ず参照してください。
実装状況と確認リンク(一次ソース)
以下に各機能ごとの一次ソース/参照先を示します。実装の存在や安定性は頻繁に変わるため、リンク先で最新情報を確認してください。
- Iterator Helpers
- TC39 提案: https://github.com/tc39/proposal-iterator-helpers
- MDN(該当部分): https://developer.mozilla.org/
-
実装状況の確認(Chromium/V8、Firefox、WebKit): 各ランタイムのリリースノート/Chrome Platform Status(該当エントリを参照)
-
Promise.try
- TC39 提案: https://github.com/tc39/proposal-promise-try
-
MDN(Promise 全体): https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Promise
-
Import Assertions / JSON Modules
- TC39 提案: https://github.com/tc39/proposal-import-assertions
- MDN(Import 文の assert 記法): https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/import#assertions
- Node ESM JSON モジュール: https://nodejs.org/api/esm.html#json-modules
ランタイムでの「いつからサポートされたか」は上記一次ソースのブラウザ互換表や各ランタイムのリリースノートを参照してください。CI に組み込む検出を推奨します(次節で具体スニペットを提示します)。
互換性表(チェックポイントと注意)
下表は導入時に必ず確認すべき観点をまとめたもので、実装バージョンは各リンク先で最新の値を確認してください。
| 機能 | 確認ポイント | ランタイム側の注意点 |
|---|---|---|
| Iterator Helpers(同期/非同期) | Object.getPrototypeOf([].values()) / async function* prototype に map/toArray があるか検出 | 一部実装はフラグや限定的な API 名で提供されることがあるため、実行時検出を必須とする |
| Promise.try | typeof Promise.try === 'function' | グローバル拡張がポリフィルで行われる場合がある。ライブラリは機能検出でフォールバックを行うこと |
| Import Assertions / JSON Modules | 動的 import('./x.json', { assert: { type: 'json' }}) が成功するか | Node は ESM 前提。バンドラはアサーションを保持しない場合があるため、ビルド時に変換プラグインを用意する |
(注)上の「確認ポイント」は CI に組み込むべき最小限のチェックです。次節でより堅牢な検出スニペットを示します。
トランスパイル/ポリフィル戦略と TypeScript 対応
この節では実務での採用戦略、ツール別設定例、TypeScript の型と tsconfig の注意点を示します。ランタイムとビルド環境に応じて手順を選んでください。
高レベル戦略
導入方針は次の観点で決めます。まずはターゲットユーザのブラウザ/Node バージョン分布を確認してください。ネイティブ前提ならトランスパイルを減らし、混在環境ならポリフィルとトランスパイルを組み合わせます。
- 最新ランタイムが主なターゲットならネイティブ利用を優先。
- 古い環境や企業環境が混在するなら、機能検出→フォールバック(ビルド時変換 or ポリフィル)を用意。
- ライブラリはグローバルを汚さない方針(内部での機能検出とフォールバック推奨)。
Babel の例(core-js を利用する場合)
Babel + core-js を使う際の基本設定例です。core-js で Iterator Helpers のサポートがあるかは core-js の changelog を確認してください。
|
1 2 3 4 5 6 7 8 9 10 11 |
// babel.config.js module.exports = { presets: [ ['@babel/preset-env', { targets: '>0.25%, not dead', useBuiltIns: 'usage', corejs: 3 }] ] }; |
注意: core-js が該当機能をポリフィルしていない場合は専用ポリフィルを組み合わせるか、トランスパイラ用のプラグインでビルド時に変換する必要があります。
esbuild / SWC / Vite / Rollup / Webpack の扱い
- esbuild / SWC: 高速だが自動でランタイムポリフィルを注入しない。必要な polyfill は明示的に import するかプラグインで注入する。
- Webpack: import assertions を扱うなら experiments.importAssertions を有効化する必要がある。
- Rollup: @rollup/plugin-json で JSON を扱う。import assertion を保存したい場合は追加プラグインが必要なことがある。
- Vite: Rollup プラグインと esbuild を組み合わせて処理。assertion の扱いはバージョン依存。
各ツールのドキュメント:
- Webpack experiments: https://webpack.js.org/configuration/experiments/
- Rollup JSON plugin: https://github.com/rollup/plugins/tree/master/packages/json
- Vite JSON docs: https://vitejs.dev/guide/features.html#json
- esbuild loader: https://esbuild.github.io/api/#loader
TypeScript の設定と注意点
推奨 tsconfig(型解決向け)例:
|
1 2 3 4 5 6 7 8 9 10 11 12 |
{ "compilerOptions": { "target": "ESNext", "module": "ESNext", "lib": ["ESNext", "DOM"], "moduleResolution": "bundler", "resolveJsonModule": true, "esModuleInterop": true, "skipLibCheck": true } } |
重要: resolveJsonModule は型(型チェック)向けの設定です。実行時に import assertions / JSON modules をサポートしているかはランタイム/バンドラ次第です。型がある=実行時に動く、ではない点に注意してください。
TypeScript:Promise.try の型拡張(再掲)
|
1 2 3 4 5 6 7 8 |
// types/promise-try.d.ts export {}; declare global { interface PromiseConstructor { try<T>(fn: () => T | PromiseLike<T>): Promise<T>; } } |
既存コードからの移行ガイド(具体例と手順)
この節は移行の優先順、具体的置換パターン、CI に組み込む検出、パフォーマンス計測方法をまとめます。段階的に進めて問題点を特定してください。
置換の具体例
- lodash.chain → Iterator Helpers(配列が大きいケースで有効)
|
1 2 3 4 5 6 7 |
// Lodash const res = _.chain(items).filter(predicate).map(fn).value(); // Iterator Helpers(配列からイテレータを作る) const it = items.values(); const res2 = it.filter(predicate).map(fn).toArray(); |
- Promise.try の置換
|
1 2 3 4 5 6 |
// 旧 const r1 = Promise.resolve().then(() => possiblyThrowingFn()); // 新 const r2 = Promise.try(() => possiblyThrowingFn()); |
- JSON モジュール導入
|
1 2 3 4 5 6 7 |
// 静的 import cfg from './config.json' assert { type: 'json' }; // 動的 const mod = await import('./config.json', { assert: { type: 'json' } }); const cfg = mod.default; |
段階的移行手順(推奨)
- ローカルで小さなサンプルで挙動を検証する(同期・非同期・例外ケース)。
- 単体テストを追加し、評価順序と例外伝播を明示的に検証する。
- CI に機能検出ジョブを追加(Node とブラウザ行列で実行)。
- 本番影響の少ないモジュールから段階的に差し替え、メトリクスを監視する。
- 問題が出たらフォールバック経路(従来実装)へロールバックする準備をする。
運用上の注意(重複排除)
導入に際して共通して注意すべき点を一箇所にまとめます。
- 副作用の発生タイミングが遅延する設計になっている。toArray 等の強制評価箇所はメモリピークを招く。
- JSON Modules ではサーバの Content-Type(application/json)や CORS が重要。静的配信環境での検証を必須にする。
- TypeScript の resolveJsonModule は型解決のみ。実行時サポートはランタイム/バンドラ次第。
- グローバルポリフィルは避け、機能検出+モジュール内フォールバックを推奨する。
- ロギング・監視を必ず用意して、導入初期に問題を捕捉する。
CI・ベンチマーク・検出スニペット(実務向け)
この節では CI に組み込める堅牢な検出スニペット、ベンチマークの測定方法、結果の解釈指針を示します。
Node 用:堅牢な JSON import assertions 検出スニペット
テスト用の JSON ファイルを一時的に作成して import を試みる方法です。ファイルが無ければ false 判定される誤検出を避けます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// scripts/check-json-import.js import fs from 'fs/promises'; import { fileURLToPath } from 'url'; const fname = new URL('../__es2025_compat_check__.json', import.meta.url).pathname; await fs.writeFile(fname, '{"ok":true}'); try { // dynamic import with assertion const mod = await import(`file://${fname}`, { assert: { type: 'json' } }); console.log('JSON_ASSERT_OK', !!(mod && mod.default && mod.default.ok)); process.exit(0); } catch (e) { console.error('JSON_ASSERT_FAIL', e && e.message); process.exit(2); } finally { await fs.unlink(fname).catch(()=>{}); } |
注: Node のバージョンによっては ESM 前提やフラグが必要です。CI ジョブは対象 Node で上記スクリプトを実行して exit code で判定してください。
ブラウザ用(Playwright など)堅牢検出フロー
- テストサーバ上に固定の JSON ファイルを配置する(Content-Type を application/json にする)。
- Playwright を使って headless ブラウザで次を評価する:
- Iterator Helpers: Object.getPrototypeOf([].values()).map が関数かどうか
- JSON import assertions: await import('/es2025_compat_check.json', { assert: { type: 'json' } }) が成功するか
サーバ未配置だと false 判定になるため、必ずテスト用ファイルを用意してください。
ベンチマークの測定方法(再現可能なテンプレート)
Node でマイクロベンチを取る場合のテンプレート。処理時間とメモリを計測します。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
// bench/iterator-helpers-bench.js const N = 5_000_000; function baseline() { const a = new Array(N).fill(0).map((_, i) => i); const out = []; for (let i=0;i<a.length;i++){ const v = a[i]*2; if (v % 2 === 0) out.push(v); } return out.length; } async function ih() { const it = new Array(N).fill(0).values(); const out = it.map(x => x*2).filter(x => x%2===0).toArray(); return out.length; } function run(fn, label) { const start = process.hrtime.bigint(); const memBefore = process.memoryUsage().heapUsed; const res = fn(); const end = process.hrtime.bigint(); const memAfter = process.memoryUsage().heapUsed; console.log(label, 'time(ms):', Number(end - start)/1_000_000, 'heapDiff:', (memAfter - memBefore)/1024/1024); return res; } run(baseline, 'baseline'); run(ih, 'iterator-helpers'); |
実行環境(Node バージョン、CPU、ヒープサイズ)で結果が大きく変わるため、CI(同一環境)で比較してください。期待されるのは「ピークメモリが改善されるケースがある一方で、関数呼び出しオーバーヘッドでスループットが低下することがある」ということです。必ず本番に近いデータで計測してください。
参考(一次ソース・ドキュメント)
各機能ごとの代表的な一次ソースを再掲します。
- Iterator Helpers (TC39 proposal): https://github.com/tc39/proposal-iterator-helpers
- Promise.try (TC39 proposal): https://github.com/tc39/proposal-promise-try
- Import Assertions (TC39 proposal): https://github.com/tc39/proposal-import-assertions
- MDN — import の assert 説明: https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/import#assertions
- Node.js — ESM: JSON Modules: https://nodejs.org/api/esm.html#json-modules
- Babel preset-env: https://babeljs.io/docs/en/babel-preset-env
- core-js: https://github.com/zloirock/core-js
- Rollup JSON plugin: https://github.com/rollup/plugins/tree/master/packages/json
- Webpack experiments: https://webpack.js.org/configuration/experiments/
- esbuild loader: https://esbuild.github.io/api/#loader
- Vite features (JSON): https://vitejs.dev/guide/features.html#json
まとめ
ES2025 の主な提案は実務上の利便性を高めますが、導入にはランタイム/ビルドツールの対応確認が不可欠です。まずはローカルで API の挙動(特に例外伝播と評価タイミング)を確認し、CI に堅牢な機能検出とベンチマークを組み込み、段階的に移行してください。
- Iterator Helpers:遅延評価の利点と toArray の強制評価に注意。非同期版は toArray が Promise を返す。
- Promise.try:同期例外を Promise に変換する簡潔なユーティリティ。ポリフィルや型宣言を用意するのが現実的。
- Import Assertions / JSON Modules:サーバの Content-Type、バンドラの設定差、Node の ESM 前提を確認する。
- 移行手順:ローカル検証→単体テスト追加→CI 機能検出→段階導入。ポリフィルは最小限に留め、ライブラリはグローバル拡張を避ける。
以上を踏まえ、一次ソースを参照して環境に合わせた導入計画を作成してください。