🎄

今後のTypeScriptに期待すること / TypeScript一人カレンダー

2024/12/25に公開

こんにちは、クレスウェア株式会社の奥野賢太郎 (@okunokentaro) です。本記事はTypeScript 一人 Advent Calendar 2024の25日目です。昨日は『Biomeを使ったLintとフォーマット』を紹介しました。

TypeScriptへの期待

TypeScript 一人 Advent Calendar 2024 最終日である本日は、TypeScriptの直近の流れを振り返りつつ「今後どんな進化があったら嬉しいか」を自由に妄想してみたいと思います。

TypeScriptはここ数年でも進化を続けている

まずは、直近の振り返りから。TypeScriptは0.8の登場から10年以上が経過し、バージョンも5.7となり十分に安定・成熟してきました。

10年前は、最初からTypeScriptを利用して開発を着手するという姿勢は先進的で、JavaScriptで開発するが段階的にTypeScriptに移行するという流れが一般的だったと思います。それは、1.xからしばらくは、まだいくつかの部分でTypeScriptの仕様限界的に表現できないことが多く存在したからでした。

それも昔の話。TypeScript 5.7の今では大半の表現を完結できるようになり、最初からTypeScriptで開発されるOSSライブラリや、TypeScriptの型推論を前提としたインタフェースを持つOSSライブラリなど、エコシステムの開発スタイルにも大きな変化が起こりました。これはTypeScriptが成熟したからこそだと思います。

しかし、進化がゆるやかになったかというとそんなことはなく、細かな改良や改善は今も絶えず行われています。たとえば、TypeScript 5.5にてArray.prototype.filterundefinednullを除去するロジックを記述すると、型推論でもそれらが除去されるという改善が追加されました。以下のコードはTypeScript 5.4での挙動を示します。

const arr = [0, 1, null, undefined, 4] satisfies (number | null | undefined)[];
const filtered = arr.filter(v => v !== undefined && v !== null);
//    ^? (number | null | undefined)[]

TypeScript 5.5以前は、filter()の結果にundefined, nullがこなそうに見えても、型の上では(number | null | undefined)[]扱いのままでした。TypeScript 5.5ではこの点が改善されています。

const arr = [0, 1, null, undefined, 4] satisfies (number | null | undefined)[];
const filtered = arr.filter(v => v !== undefined && v !== null);
//    ^? number[]

これは何年も前から多くのTypeScript利用者にとても待ち望まれていた改善です。ほんの些細な進化かもしれませんが、こうした一つひとつの改良がTypeScriptをさらに使いやすくしています。

こうした長らく望まれていた改善は、TypeScript 5.0以降でも継続的に実施されています。TypeScriptは現在とても安定していますが、まだまだ進化の余地を残しているのです。

今後を自由に妄想

ここからは筆者が無根拠に、TypeScriptについてあれこれ妄想してみます。これらは全て、筆者がTypeScriptを何千時間と書いてきた中で感じた項目です。

NonNullable<T>のsugar記法がほしい

TypeScriptを使っていて、NonNullable<T>を多用するケースは多いです。例えば、GraphQLのレスポンスの推論との組み合わせなどで、NonNullable<T>を何度も入れ子にして書くことがあります。

// 入れ子が多い
type Nested = NonNullable<NonNullable<NonNullable<Foo>['bar']>['baz']>;

のように何重にも NonNullable を書くことになり、「T! 記法があったら便利なのに」と思うことがあります。?. 演算子と似た発想で ! を使って記述できたらスッキリするのではと妄想したり、TypeScriptのRepositoryを検索してみたりするのですが、この提案はたびたび出ながらも採用には至っていません。

// こう書いてみたいよね…
type Nested = Foo!['bar']!['baz']!;

ConstraintOf<T>のような型制約を取り出すユーティリティ型

標準のユーティリティ型には、Uppercase<S> のように「文字列型リテラルを大文字化する」という型変換が用意されていますが、これらは「S extends string」という制約を持ちます。ところが、その制約部分(string)を抽出する手段はTypeScriptには用意されていません。

たとえばUppercase<S>の元の定義は次のようになっています。intrinsicはユーティリティ型の組み合わせだけでは達成できない特殊な操作をコンパイラが直接実装し、それを実行していることを示すキーワードです。

type Uppercase<S extends string> = intrinsic;

この型パラメータSにおけるextends stringを取り出すConstraintOf<Uppercase>のような仕組みがあれば、既存の.d.tsを引用するラッパーなどの実装で型の多重定義をする必要がなくなるのに、という思いがあります。

// こんな型は存在しません
type Constraint = ConstraintOf<Uppercase>;
//   ^? [string]

ただし現状、ConstraintOf<T>はあくまでも予想止まり、実装予定は聞こえてきません。

高カインド型も欲しい

さらに言えば、高カインド型 (Higher Kinded Types, HKT) のサポートがあると型操作が飛躍的に面白くなります。

そもそも、前述の妄想コードはエラーとなります。それは、Uppercase<S>型について、Uppercaseだけを記述してもそれは単なる型パラメータ不足になってしまうからです。

type Constraint = ConstraintOf<Uppercase>;
// Generic type 'Uppercase' requires 1 type argument(s).(2314)

この記述を許容できると面白くなりそうです。例えば以下のように書くことは現状できませんが、これが出来ると幅が広がります。

// この妄想コードはエラーになります
type Alias = Promise;
type PromiseString = Alias<string>;

もし「型コンストラクタ自体を型パラメータとして扱う」ことができるようになれば、ジェネリック型のパラメータにジェネリック型を渡し、より高次の操作が可能になります。ReturnType<T>が返す値が型パラメータを含む型の場合に、こんな書き方ができるかもしれません。

ReturnType<T><U, V>;

しかしこれも妄想止まりです。

型のパターンマッチも欲しい

Conditional Typesで三項演算子が入れ子になると、可読性が下がりがちです。何個もの?:が交互に現れ、いま何について分岐しているのか、とても見通しが悪くなります。そこで、パターンマッチ構文も妄想したくなります。

// こんな構文は存在しません
type Match<T> = match T {
  when [infer U, ...infer Rest] /* 配列orタプルなら何か */,
  when { x: infer X } /* オブジェクトなら... */,
  when _ never
};

これはTypeScriptの型プログラミングに求めるよりも先に、そもそもECMAScript TC39にてproposal-pattern-matchingも通してほしいものです。Pattern MatchingのStageは1、まだまだ仕様化には遠いです。

TC39提案の動向

TypeScriptの進化を妄想するときに、ECMAScript TC39にはどんなプロポーザルが提出されているかを眺めるのも一つの手です。

TC39には、他にもCall this operatorPipeline operatorなど、もし採用されたら大きくプログラミングスタイルを変えるだろう提案が進められています。

たとえばPipeline演算子 (|>)やCall-this演算子 (~>)が正式に入れば、関数を連続して扱う処理が格段に書きやすくなるでしょう。オブジェクト指向プログラミングそのものの書き方が大きく変化するかもしれません。こうした提案がStage 3に入るようであれば、TypeScriptにも何らかの影響や仕様変更が及ぶ可能性があります。

以下の資料は、これらの構文が与えてくれる魅力や使い勝手の変化について触れられている、とてもユニークな内容で筆者はとても気に入っています。

WASMの進化はどうなるか

WebAssembly (WASM) が登場してから約10年。パフォーマンスが要求される領域では、ゲームやエディタ系でWASMの活用事例がみられますが「Webアプリケーション開発において、日常的に誰もが使う」という域にはまだ達していない感覚があります。

一方で、JavaScript自体もV8などの最適化が進んで、それ自体も初期のJavaScriptの遅さから比較すると劇的に高速化したため、実装内容によってはJavaScriptで十分実用レベルという状況もあります。

そのため「これからのWASMはどうなるのか?」という話題は、高速化観点に限らずとても興味深いところです。

WASMとJSを両方出力する言語が流行る?

WASM単体ではなく、WASMとJavaScriptのグルーコードを一括で出力する新言語というものが生まれてもおかしくないと考えています。WASMの出力が可能な言語は今や珍しくないですが、グルーコードとなるJavaScriptコードも同時に出力することを前提とした単独の言語というのは、まだ見た覚えがありません。

似た事例としてはRust向けのツールとしてwasm-bindgenというものがあります。これはグルーコードとなるべきJavaScriptファイルも同時に生成しています。考え方としては納得感のある手法であるため、WASMとJSを両方出力することを「前提」とした次世代のWebプログラミング言語が現れたりするだろうかと興味深いです。

TypeScriptと非常に近い構文でWASMを出力する言語としてはAssemblyScriptという事例があったり、Rustに比較的近い構文を持つWASMの出力に特化したMoonBitのように、WASM界隈のエコシステムも流れを感じます。

とはいえ、現時点で多くのWebアプリは「TypeScript + HTML + CSS + Webブラウザ」という枯れきったエコシステムで回っており、まだあと5年、いや10年後もこのスタイルが生き残っている可能性は考えられます。

遥か未来のWebアプリケーション開発

Webの誕生からすでに30年以上が経過し、HTTP, HTML, CSS, JSという基盤はいまだに世界の標準として使われ続けています。

ここから「まったく新しいプロトコルや現存のWebブラウザとはまったく互換性のない次世代ブラウザが登場し、現行のWebから一切合切が置き換わる」というのは非常にコストが高く、妄想するにしてもさすがに非現実的なようにみえます。

COBOLが60年使われ続けているように、巨大なインフラやレガシーを一気に無くすことは難しく、Webも誕生からの30年の延長線上を今後も歩み続け、当面は発展しながら継続する道を辿ると予想しています。ただし、その道中でそのWebを制御するためのプログラミングパラダイムは変化し続けるはずです。

TypeScriptも、時代に合わせて仕様や機能をアップデートし続けていくでしょう。

Webブラウザ界に一波乱あるか

とはいえ、米司法省によるGoogle解体、ChromeやAndroidの分離という流れは全世界のWeb業界従事者が気になるところです。これは2024年の特徴的なニュースでした。

単なるChromeの分離だけでなく、Firefoxを開発するMozillaの収益性悪化など、連鎖的な影響が予想されWebに一波乱起こるのではないかと注目しています。

また、ブラウザエンジンにも動きがありました。GitHubの創設者であるChris Wanstrath氏と、オープンソースのPC向けOSであるSerenityOSの開発者・Andreas Kling氏が、非営利団体の「The Ladybird Browser Initiative」を立ち上げ、Blink, Gecko, WebKitなどの大手ブラウザエンジンとは別に、独立したLadybirdというWebブラウザプロジェクトを立ち上げました。

Ladybirdプロジェクトは「Google広告費の影響を受けない新たなWebブラウザが必要」と謳われており、あまりにも巨大になったGoogleの牙城にどう変化が起こるのかは、今後のWebを見守る上で重要でしょう。

AIフレンドリーなコード

筆者が2022年のアドベントカレンダーを書いたあと、2023年以後ChatGPT, GitHub Copilot, ClaudeをはじめとしたAIが急速に普及しました。Microsoft Copilot, Gemini, Apple Intelligenceなど、この2年で各OSベンダーもAIをプラットフォームに標準で組み込むようになりました。

今後、AIがコードを書くのはおそらく当たり前になると思われます。人間も書くでしょうが、大半はAIが書いてしまいます。この流れは変わらないと予想しています。そこで、AIがコードの大半を自動生成するようになったとき、今度はプロンプトの品質が重視されるようになるでしょう。いい加減な指示を出すといい加減なコードが出力される時代がしばらく過渡期的に続くと予想しています。そのとき、マシンリーダブルなコードを提供するという概念がさらに大切になるかもしれません。

限られたコードの断片からAIが正しくコードを理解しようとするとき、型の追跡性は重要になると考えています。TypeScriptにはすでに Parameters<T>ReturnType<T> など多くのユーティリティ型がありますが、こうした型を扱うことで「定義の出典」を分かりやすく書いておくというアプローチは今からでも可能です。

解釈の余地の少ない厳格な定義は、AIが読みやすいでなくもちろん人間にとっても読みやすいため、厳格に記述するという姿勢は、今後も重要であり続けることに変わりはないと予想しています。

TypeScript 一人 Advent Calendar 2024のしめくくり

ここまで、TypeScriptの直近の動向や、今後欲しい機能の妄想、数年後の展望や予想などをお話ししてきました。

筆者はTypeScriptが大好きであると同時に、Webアプリケーション開発そのものが大好きです。膨大な「気にするべきこと」をプログラムで整理し「やりたいこと」を次々と実現していく過程は、まさに快感そのものだと感じています。「概念や秩序を整え、つくりたいものを生み出す」ためにも、筆者はこれからもTypeScriptの習得を絶やしません。

今回のカレンダー25記事と、2年前に書いたカレンダーの25記事をあわせて、これにて50記事が出揃いました。2年前の記事でも、まだまだ現在通用するテクニックを豊富にご紹介しています。ここからTypeScriptのちょっとした工夫の引き出しを増やし、TypeScriptによる開発をより楽しんでいただければ幸いです。このカレンダーからもし何か学びがあったのであれば、筆者としてこれ以上ない喜びです。

TypeScript 一人 Advent Calendar 2024は以上です、ありがとうございました。それでは、良いお年を。

Discussion