本セッションの登壇者
セッション動画
皆さまこんにちは、株式会社バベルにてプリンシパルエンジニアをしておりますuhyoです。まもなくリリースされる予定のTypeScript 5.0の新機能が、この発表ですべてわかるように用意しましたのでぜひお聞きください。
TypeScriptのリリースサイクル
まずはTypeScriptのリリースサイクルをおさらいしておきましょう。TypeScriptは3カ月に1回のリリースサイクルを採用しており、TypeScript 5.0もそのサイクルに則り、2023年3月に公開される予定です(3カ月に1回といいつつ、4.9から5.0まで4カ月空いているのは年末に1カ月お休みしていたからだと思います)。
なお、TypeScriptはSemantic Versioningを採用していないので、4.9の次のバージョンは5.0ということになり、5.0が他のバージョンと比べて特別大きなリリースというわけではありません。
TypeScript 5.0の概観
それではTypeScript 5.0がどのように進化したかを見ていきましょう。
大きな変更のひとつはデコレータの実装です。それ以外にもTypeScript 5.0にはオプションの追加/変更が多く含まれ、とくにバンドラに関するサポートが充実します。
Stage 3デコレータの実装
デコレータは昔からあるのでご存知の方も多いかと思いますが、その仕様が固まってきたのは比較的最近です。デコレータのプロポーザルがStage 3になって仕様も安定し、いよいよTypeScriptにデコレータが実装されます。
実はTypeScriptには昔から古い仕様でデコレータが実装されて使われており、このサポートを終了することはできないので、古い仕様のデコレータはLegacy Decoratorsとして共存することになります。幸い、古い仕様のデコレータを使うためにはexperimentalDecoratorsというコンパイラオプションを有効にする必要がありました。TypeScript 5.0以降では、experimentalDecorators: trueのオプションがあればLegacy Decoratorが使えて、オプションがなければ新しいStage3 Decoratorsが使えるようになります。
古いオプションの非推奨化が始まる
これはTypeScript 5.0での新しい試みですが、TypeScriptには古くて意味のないオプションも増えてきており、これらを廃止するためのスケジュールが組まれました。10回のリリースを経て廃止するという長期の計画です。
具体的に廃止されるオプションは以下のとおりです。
- 需要がなくなったもの
- 型システムの進化で必要がなくなったもの
- 昔TypeScriptの型チェックが厳しくなったときに、移行用に一時的にチェックを無効化するために作られたもの(TypeScript 1.xなどの自体のオプションで、さすがに移行が完了していそうなものもある)
.tsによるimportが解禁
これには驚いた方も多いかと思いますが、TypeScript 5.0ではimport {...} from "./foo.ts"のように.tsによるimportが解禁されます。ただし制約があり、JavaScriptにトランスパイルするような設定では相変わらず.tsによるimportは許可されません。noEmit: trueやemitDeclarationOnly: trueなど、JavaScriptにトランスパイルしないときには.tsによるimportが解禁されます。
これまでも.tsによるimportのFeature Requestはありましたが、TypeScriptは頑なに拒んできました。なぜかというと、トランスパイル後に.jsに直すという作業をTypeScript側がしたくなかったためです。
冒頭で「TypeScript 5.0ではバンドラに関するサポートが強化される」とお伝えしましたが、バンドラは.tsを.jsにトランスパイルしなくてもバンドルしてくれる(.tsのままモジュール解決してくれる)ので、.tsによるimportを許可してもよいのではないかということで解禁されました。ただし、.tsによるimportを使うためにはコンパイラオプションのallowImportingTsExtensionsを有効化する必要があります。
モジュール解決に関する設定の追加
TypeScript 5.0ではmoduleResolution: "bundler"という設定が導入され、その挙動を細かく設定するためのオプションとしてresolvePackageJsonExports / resolvePackageJsonImports / customConditionsがあわせて実装されます。
モジュール解決に関するバンドラの特徴は
- TypeScriptファイル間で直接import/exportができること
- package.jsonのimports/exportsフィールドを解釈できること(
subpath exportsなどを実現できる)
の2点ですので、moduleResolution: "bundler"とするとこれらの機能をTypeScriptで使えることになります:
- .tsや拡張子なしのimport
- package.jsonのimports/exportsの解釈
package.jsonのimports/exportsはかなり便利ですが、もともとNode.jsの機能だったため従来はmoduleResolution: "node16"というオプションを使わなければpackage.jsonのimports/exportsは解釈されませんでした。
しかし、最近はバンドラでもpackage.json関連の機能をサポートしているため、それに合わせてmoduleResolution: "bundler"でもpackage.json関連の機能が有効化されることになりました。
なお、Node.jsは拡張子を.jsとしなければモジュール解決ができず、従来moduleResolution: "node16"を使うときも同様に拡張子が必要で、バンドラ環境で使うのが難しい状態でした。TypeScript 5.0からはmoduleResolution: "bundler"を使用すればpackage.jsonのimports/exportsと拡張子なしのimportが両立できるため、バンドラ使いにとっては便利になります。
任意の拡張子をimportするオプション
TypeScript 5.0ではallowArbitraryExtensions: trueオプションを使うことで、import "foo.css"のように任意の拡張子をimportできるようになります。
これを聞くと「前からできたのでは?」と思われる方も多いでしょう。このオプションはmoduleResolution: "node16"では、Node.jsでのimport時に拡張子.jsが必要だったため、それ以外の拡張子のimportは許可されなかったという背景から追加されました。
allowArbitraryExtensions: trueオプションで任意の拡張子をimportできるようになると、型定義に関する問題が出てきます。従来もfoo.cssなどもrequireできましたが、CommonJSではfoo.css.jsをimportしているのに等しいと解釈されてきました。したがって、foo.css.d.tsでfoo.css.jsに対する型定義を書いていたことになります。
しかしNode.jsのES Modulesでは、foo.cssとfoo.css.jsそれぞれに対して別々の型定義を用意しなければいけないはずです。現在書けるfoo.css.d.tsはfoo.css.jsに対する型定義で、foo.cssに対する型定義を書くための機能はTypeScriptにこれまで存在しませんでした。
そこでTypeScript 5.0ではfoo.cssに対する定義ファイルfoo.d.css.tsを書くための機能も追加されます。これは新しい概念ですので、もしNode.js向けに任意の拡張子のファイルをimportするTypeScriptプログラムを書くことがあれば、覚えておいたほうがよいでしょう。
CommonJSの時代は拡張子はあまり真剣に考慮する必要がありませんでしたが、Node.jsのES Modulesをサポートするにあたり、このような拡張子の細かいポイントまでTypeScript側で考慮する必要が出てきました。
総評 - 具体的なユースケースに応えたTypeScriptの進化
TypeScript 5.0では、Node.jsでの非JSファイルの読み込みやバンドラなど、具体的なユースケースを念頭においた機能追加が多い印象です。最近のTypeScriptは独自のルール/仕様を避ける傾向にあり、Feature Requestもユースケースが重視されるようになっています。そのような傾向もあり、バンドラに関するユースケースが採用され、具体的なユースケースを念頭においたサポートが追加されたことはとてもありがたいです。
個人的には、バンドラでpackage.jsonのexportsを使いたい場面が多かったので、TypeScript 5.0でこれが解禁されるのはとても嬉しいです。
以上で発表を終わります。どうもありがとうございました。