MicrosoftはTypeScript 5.8の正式版を発表した。「require()」を使用したESMのサポートや、新しい「--module node18」フラグなどが追加された。
この記事は会員限定です。会員登録(無料)すると全てご覧いただけます。
Microsoftは2025年2月28日(米国時間)、TypeScript 5.8の正式版を発表した。同年2月11日に公開されたリリース候補(RC)版からの変更はなく、RC版で追加された機能が反映されている。本稿では4つの新機能の概要を説明する。
RC版の使用を開始するには、以下のnpmコマンドでインストールできる。
npm install -D typescript
下記のコードを例に挙げる。
declare const untypedCache: Map<any, any>; function getUrlObject(urlString: string): URL { return untypedCache.has(urlString) ? untypedCache.get(urlString) : urlString; }
このコードの目的は、キャッシュ内にURLオブジェクトが存在する場合はそれを取得し、存在しない場合は新しいURLオブジェクトを作成することだ。しかし、このコードにはバグがある。入力から新しいURLオブジェクトを実際に構築する処理を入れそびれている。従来のTypeScriptでは、この種のバグを検出できなかった。
TypeScriptが「cond ? trueBranch : falseBranch」のような条件式をチェックする際、その型は2つの分岐の型のユニオン型として扱われる。つまり、「trueBranch」と「falseBranch」の型を取得し、それらを組み合わせてユニオン型を作る。この場合、「untypedCache.get(urlString)」の型は「any」で、「urlString」の型は「string」だ。ここで問題が発生する。「any」は他の型と組み合わさると、非常に影響力があるため、「any | string」は単純に「any」に変換される。その結果、TypeScriptが「return」文の式を関数の戻り値の型である「URL」と互換性があるかどうかを照合する際に、型システムはこのコードのバグを検出するための情報を失ってしまう。
TypeScript 5.8では、型システムは「return」文内に直接書かれた条件式に対して特別なチェックが入る。条件式の各分岐が、関数の宣言された戻り値の型(もし存在すれば)と照合されるため、上記のコードのバグを検出できるようになった。
declare const untypedCache: Map<any, any>; function getUrlObject(urlString: string): URL { return untypedCache.has(urlString) ? untypedCache.get(urlString) : urlString; // ~~~~~~~~~ // error! Type 'string' is not assignable to type 'URL'. }
長年にわたり、Node.jsはCommonJS モジュールと並んでECMAScriptモジュール(ESM)をサポートしてきた。しかし、両者の相互運用性には幾つかの課題があった。
つまり、ESMファイルからCommonJSファイルを使用することは可能だったが、その逆は不可能だった。この制限により、ESMサポートを提供したいライブラリの開発者にとって大きな課題が生じた。ライブラリの開発者は、CommonJSユーザーとの互換性を壊すか、ライブラリを「デュアルパブリッシュ」(ESM用とCommonJS用のエントリーポイントを別々に公開)するか、あるいはCommonJSに無期限でとどまるかのいずれかを選択せざるを得なかった。一見するとデュアルパブリッシュが妥協案のように思えるが、これは複雑でエラーが発生しやすいプロセスであり、パッケージ内のコード量がほぼ倍増するという欠点がある。
Node.js 22では、これらの制限の一部が緩和され、CommonJSモジュールから ECMAScriptモジュールへの「require("esm")」呼び出しが許可されるようになった。ただし、トップレベル「await」を含むESMファイルは引き続き「require()」できないが、それ以外のESMファイルはCommonJSファイルから使用可能になった。この変更により、ライブラリの開発者はデュアルパブリッシュなしでESMサポートを提供できるようになる。
TypeScript 5.8は、この動作を「--module nodenext」フラグの下でサポートする。「--module nodenext」を有効にすると、TypeScriptはESMファイルへの「require()」呼び出しでエラーを発生させない。
この機能は古いバージョンのNode.jsにバックポートされる可能性がある。そのため、この動作を有効にする「--module nodeXXXX」オプションの安定版は、2025年3月時点では存在しない。しかし、将来のTypeScriptのバージョンでは、「node20」の下でこの機能が安定する可能性がある。当面の間、Node.js 22以降を使用しているユーザーには「--module nodenext」を推奨する。一方で、ライブラリの開発者や古いNode.jsバージョンを使用しているユーザーは、「--module node16」を維持するか、「--module node18」にマイナーアップデートすることを推奨する。
TypeScript 5.8では、新たに安定した「--module node18」フラグが導入された。Node.js 18 を固定して使用するユーザーにとって、このフラグは「--module nodenext」に含まれる特定の動作を除外した、安定した参照ポイントを提供する。具体的には、以下の違いがある。
最近、Node.js 23.6 では、TypeScriptファイルを直接実行する実験的なサポートがフラグなしで利用可能になった。ただし、このモードでは特定の構文のみがサポートされる。Node.jsは「--experimental-strip-types」というモードのフラグを解除しており、このモードではTypeScript固有の構文がランタイムのセマンティクスを持たないことが要求される。言い換えれば、ファイルからTypeScript固有の構文を簡単に削除(strip out)することができ、有効なJavaScriptファイルとして残せる必要がある。
そのため、次のような構文はサポートされない。
以下は、動作しない例だ。
ts-blank-space やAmaro(Node.jsにおける型削除の基盤ライブラリ)など類似のツールもあるが、これらのツールも同じ制約を持つ。これらのツールは、要件を満たさないコードを検出するとエラーメッセージを提供するが、実際に実行するまでコードが動作しないことに気付かない可能性がある。
そこで、TypeScript 5.8では「--erasableSyntaxOnly」フラグが導入されている。このフラグを有効にすると、ランタイムの動作を持つ、ほとんどのTypeScript固有の構文に対してTypeScriptはエラーを出す。
class C { constructor(public x: number) { } // ~~~~~~~~~~~~~~~~ // error! This syntax is not allowed when 'erasableSyntaxOnly' is enabled. } }
通常、このフラグは--verbatimModuleSyntaxと組み合わせて使用することが推奨される。これにより、モジュールが適切なimport構文を持ち、importが省略されないことが保証される。
Copyright © ITmedia, Inc. All Rights Reserved.