
11,408 文字

今日はウェブ開発の世界で大きな一日です。タイトルは全くクリックベイトではありません。それほど重大なことなので、私は今日の朝7時に起きてこれを実現させました。私の睡眠スケジュールを知っている人なら、それがいかに異常なことかわかるでしょう。今日、Microsoftは私が見ることになるとは思わなかったものを発表しました。TypeScriptタイプチェッカーのネイティブ移植版です。そして明確にしておきますが、これはコンパイラだけではなく、TypeScriptをJSに変換する部分だけではありません。そういったものは他の言語でも見てきました。これはタイプチェッカーです。コードベース内の各タイプが正確で正しいことを確認するものです。これは歴史的に他の言語では不可能に近いことでした。私はそれは不可能だとさえ言っていましたが、間違っていたことが証明されました。これは素晴らしいプロジェクトです。TypeScriptチームが早期に私に連絡をくれて、見る機会を得られたのは幸運でした。このプロジェクトについて学んだすべてのことを共有できることをとても楽しみにしています。TypeScript Goは、TypeScriptエコシステム全体にとって大きな変化であり、これについて知れることはすべて知っておくべきです。
どれくらい速くなるのか気になる方のために言うと、約10倍速くなります。なぜRustを使わなかったのか気になる方は、スポンサーの後までお待ちください。
Reactはアプリ構築をはるかに簡単にしたと言っても過言ではないでしょう。少なくとも半分は。フロントエンド側ですね。API側ではあまり役立ちません。GraphQLなどで解決しようとしてきましたが、正直あまり役立っていません。その抽象化レベルはコードを改善するどころか、実際にはかなり悪化させます。また、APIを生成するためにチェックボックスをクリックするような派手なUIを作る人たちも見てきましたが、それはさらに悪いです。一瞬真面目に考えましょう。物事を簡単にしたいなら、バックエンドの姿を本当に再考する必要があります。それが今日のスポンサーであるConvexがやろうとしていることです。
彼らは自分たちを「Reactアプリの足りない半分」と呼んでいますが、正直同意します。Reactを本当に理解し、リアクティブなアプリケーションを作りやすくする初めてのバックエンドソリューションです。To-doリストのような何かでさえ、実際にはずっと簡単になります。とてもシンプルなものに見えるかもしれませんが、バックエンドを追加して、あるところでチェックされたものが他の場所ではチェックされていないという状況を適切な更新で処理するようにするのは、思ったほど簡単ではありません。Convexを使えば、これをtrueまたはfalseに設定するのは文字通り1行のコードで済みます。Convexフォルダで定義するこのsetComplete関数は、コードベース内の実際のコードであり、クライアントでインポートして期待どおりに動作します。フロントエンドからバックエンドまで完全に型安全で、ライブ同期機能付きです。
このようなライブ同期を実現するのがどれだけ難しいか知っていますか?楽しくないですよ、必要がなければ扱わないでください。文字通りto-dosをカウントするだけなら、usequery api.too.listはtrpcに似ていますが、バックエンド全体はすでに構築されています。通常はロックインを心配する必要がありますが、Convexではそうではありません。完全にオープンソースで、セルフホスト用のガイドもたくさん公開されました。
認証からファイルホスティング、データベース、サーバー機能、WebSocket、ライブアップデートまで、バックエンドのすべての部分を把握するのに疲れているなら、Convexは最も簡単で、最も安価で、最も速い方法で、それについて心配する必要がありません。ユーザーに素晴らしいUIを構築したいなら、そういったことに悩むのをやめて、Convexを試してみてください。これまで以上に速く進めるようになります。今日so.l/cconvexでチェックしてみてください。
Andersがこれを書いたのが大好きです。彼はかわいいYouTubeビデオも作りました。この動画では、そのビデオからの短いクリップや瞬間が共有されますが、この動画を見終わったら、Andersのビデオを見ることを強くお勧めします。本当に良いビデオです。TypeScriptの主な価値提案は、コードベースが成長するにつれて優れた開発者体験を提供することです。TSそのものの価値も高まります。しかし多くの場合、TypeScriptは非常に大きなコードベースにまでスケールアップすることができませんでした。
このブログ投稿の初期版を読みましたが、フォーマットはかなり良くなっています。少しフィードバックを提供しましたが、すべてが統合されているのを見るのは素晴らしいです。実際に気にすべきなのは、VS Codeのコードベースでのテストで、コンパイル時間が77秒から7.5秒に変わったという性能の変化です。Playwriteのような大きなコードベースでは、11秒から1.1秒に短縮されました。
ここでのtrpcの言及も気に入っています。trpcクライアントは9.1倍速くなります。大きなコードベースでtrpcを使ったことがある人なら、これがどれだけゲームチェンジャーになるか分かるでしょう。とてもワクワクします。彼らはこれが機能的に完全ではないと言っていますが、だからといって実際のコードで今すぐ実行できないわけではありません。彼らはバイナリを配布していませんが、私はすでにGoをセットアップして、ローカルでコンパイルし、どれだけパワフルなのかをお見せできるようにしました。
これはビルドとタイプチェックの時間だけに影響するわけではありません。エディタのパフォーマンスも大幅に向上します。IDEはインテリセンス機能を使うときに実際にTSCを使用しています。何かにカーソルを合わせてそのタイプを確認するとき、それは基になるタイプを知るためにTSCを使用しています。また、保存せずにコードを編集して、変更されたタイプや、コードの何が正しいか間違っているかを確認できる理由でもあります。それは簡単なことではなく、パフォーマンス良く実行するのはさらに難しいことです。
彼らはすでにVS Codeでこれを機能させており、もう一つ素晴らしい詳細として、従来のLSP(Language Server Protocol)に移行しています。LSPはVS Codeが構築した概念で、他のプログラミング言語とエディタへのフィードバックを追跡するためのプロトコルを使って統合しやすくするためのものです。TypeScriptはLSPが発明される前に作られたため、TypeScriptはVS Codeに組み込まれていました。今では他のすべての言語と同じように伝統的な方法でTypeScriptを扱うようになります。特別な扱いはなくなります。LSPは標準的な方法であり、この再構築の恩恵は他のエディタでも見られるでしょう。Neovimユーザーの皆さん、かなり期待してもいいですよ。
最初に試したテストはTypeScriptコードベースで、診断コールでTSCを呼び出し、報告された時間を確認し、その直後に自分たちでも実行できます。ここで見られるように、ユーザー向けの時間で約60秒、適切な合計実時間で40秒で実行されました。しかし、新しいものと比較すると、エラーが発生しますが、そのエラーにもかかわらず、ユーザー時間1.56秒で一気に通過しました。私にとって、これは60秒に感じましたが、新しい方は2秒に感じました。かなり驚異的です。
残念ながら、多くのタイプエラーなしでPlayrightをコンパイルすることができていません。公式のTSCで実行すると約4秒かかりますが、新バージョンでは0.5秒です。これは驚異的です。このバージョンはもう少し多くのタイプエラーがありますが、生の移植であることを考えると、それほど多くはありません。190ではなく213個のエラーを見つけました。これはかなり接近しています。彼らが示したテストを正確に再現できないのは悲しいですか?はい、でも、とにかく実行できるという事実自体が驚異的です。そして、これまで見てきたものからすると、これらの数字は一貫しています。かなり一貫して10倍のパフォーマンス向上が見られます。
このような性能差を得るためには、書き直しにおいて何かクレイジーなことが起きているはずですよね?つまり、すべてのロジックを完全に書き換えて再考したに違いありません。いいえ、実際にはGoを選んだ大きな理由の一つです。ここでCheckerコードを見てみましょう。ちなみに、CheckerはTypeScriptチェッカーのすべてを定義するメインのTypeScriptファイルで、明らかにTypeScriptで書かれています。これは50,000行以上のコードファイルです。特定の関数を選びます。getTypeOfSymbolは、与えられたシンボルが何であるべきかを決定するために非常によく呼び出される関数です。この関数はこのように見えます。比較的シンプルで読みやすいですね。
Goでの同じ関数と比較してみましょう。あれ?これらがほぼ同一であることに気付くかもしれません。TypeScriptチームはネイティブプラットフォームで最高のパフォーマンスを得られる言語を見つけるために、言語の書き方を大きく変えることなく、同時実行性と並列性を持つ言語を見つけるために努力しました。この初期バージョンのほとんどのコードは、彼らが書いたスクリプトによって処理され変換されました。彼らはTypeScriptコードをGoに移植するためのスクリプトを書きました。明らかに完璧ではなく、構文は正しくコンパイルできるようになりましたが、挙動はかなり正確ではなかったため、それ以来かなりの手直しが必要でした。しかし、その結果、TypeScriptコードベースで長く働いてきた開発者が、これらの変更を加えるのは比較的簡単です。
私たちの誰もがこれをできるというわけではありません。TypeScriptコンパイラのTypeScriptバージョンでの作業は信じられないほど難しく、それをパフォーマンス良く動作させるために行ったこと、JavaScriptVMの知識、そして機能させるために行ったすべてのハックは信じられないものです。しかし彼らはそれをやり遂げ、TypeScriptバージョン自体から得られたパフォーマンスはかなり驚異的でした。しかし、パフォーマンスを最適化するための変更を加えていない生の移植でも、すでに大きな勝利が見られています。
そして、これを指摘しておく必要があります。GitHubでそのChecker.tsファイルにアクセスしても、ファイルが大きすぎてレンダリングされません。単一のTypeScriptファイルで3メガバイトもあるのです。だから私はエディタで開いていました。
誰もが至る所で尋ねている質問について話す時が来ました。なぜRustではないのか?まず、自分自身の言葉を引用します。「Goはおそらく、ほとんどのJavaScriptビルドおよび開発ツールに適した言語です。」これは昨年2月、1年以上前に言ったことですが、結構うまく年を重ねていると思います。なぜでしょうか?正直に言うと、これ自体を完全に別の動画にしたい誘惑に駆られていますので、ここでは概要をお伝えし、もっと詳しい情報が欲しい方は後で詳しく説明したいと思います。
最初の主なポイントは、明らかにJSに似たよりシンプルな構文です。先ほど示した例でそれを見ました。もう一つのおそらくはるかに重要な部分は、より柔軟な構造と型システムです。TypeScriptを非JS系の言語で書くのが本当に難しかった理由の一つは、JS自体が非常に柔軟だからです。いつでもどんなものにもプロパティを追加することができます。オブジェクトがあれば、別のキーを追加するだけで処理してくれます。そのため、型チェックのステップにも同様の柔軟性が必要です。あなたのツールがそれに準備ができていれば、頑張ってください。
しかし、もう一つ本当に大きな部分があります。同時実行と並列処理です。JavaScriptはシングルスレッドの言語です。人々はそれをTypeScriptとJavaScriptが遅い理由として指摘することがあります。しかし、実際には多くのアプリケーションコードに対しては速い傾向があります。V8(主要なJavaScriptランタイム)の魔法は、そのスレッドがイベントループのおかげで信じられないほど高いパフォーマンスを発揮できることです。I/Oや他のバックグラウンドプロセスでブロックされている場合、データベース呼び出しを待っているとか、ファイルの読み込みを待っているとかの間に、プロセス内で他のことができます。そのため、ユーザーは他のことを待っている間でも入力して即座に応答を得ることができます。
しかし、複数のことを同時に行うことはできません。これはWebサーバーのようなものには問題ありません。Webサーバーは主にランダムなことが起こるのを待っていて、リクエストとレスポンスを非常に素早く処理できるからです。だからNodeは実際にかなり良いバックエンド言語です。しかし、コンパイルのようなマルチスレッドから大きな恩恵を受けるワークロードには向いていません。
私の言葉だけを信じないでください。これはTypeScriptプロジェクトの主任アーキテクト、Andrew Halbergの言葉です。「JavaScriptランタイムプラットフォームは、UIとブラウザ使用に最適化されており、コンパイラやシステムレベルのツールのような計算集約型ワークロードにはあまり向いていません。おそらくJavaScriptから絞り出せる限界に達しています。」
Goをけなすのが好きで、信じてください、私はGoangのファンではありません。Twitchでそれを書くのがとても嫌いだったので、誤ってフロントエンド開発者になってそれを避けようとしました。しかし、これには本当に適しています。なぜなら並列処理が言語プリミティブとして組み込まれているからです。どの関数でもいつでも遅延させることができ、物事を分割して共有メモリで共有するのがとても簡単です。JavaScriptでWebワーカーを使用することもできますが、そのワーカーはメモリを共有できません。イベントバスを介してJSONとして物事を渡さなければならず、そのJSONのシリアライズとデシリアライズにより、シングルスレッドで行うよりもパフォーマンス特性が悪化します。
最大の勝利は、パフォーマンス向上の50%以上が並列型チェックを実行する能力から来ているようです。ただし、並列処理は考えられるほど簡単ではありません。型チェックしているシステム内のどのファイルも、多くの他の場所で参照される可能性があるからです。そのため、並列処理は実際には多くの冗長な作業を行っており、どのスレッドセットも同じファイルをチェックしている可能性があります。異なる場所から始まっても同じです。
しかし、最終的にはエラーを重複排除して、ユニークなものだけを表示できるので、うまくいきます。同じファイルを異なるスレッドで15回チェックしても、エラーは1つだけになります。そしてそれがはるかに速いので、結局はどちらにしても良くなります。そこでの唯一のコストはメモリ使用量であり、ものを再チェックしない場合よりもメモリは高くなりますが、それでもJSバージョンよりも約50%少ないメモリ使用量です。それも、メモリを最適化するための作業は何もしていないにもかかわらずです。かなり驚異的です。
ここで最後に非常に簡単に触れておきたいポイントが一つあります。これについての動画を作るなら、基本的にこのポイントに専念することになるでしょう。ガベージコレクションです。ガベージコレクションはアプリケーションのメモリを管理するための戦略です。一般的に言えば、GCはもう使われていないものを見つけて、そのメモリを解放するプロセスです。Rustのような言語では、自分でメモリを管理する必要がありますが、Rustにはボローチェッカーもあり、何が使用中か使用中でないかを知るのがはるかに簡単になるので、物事を解放し続けるのは些細なことです。
しかし、どのメモリにアクセスしているかを常にチェックする必要があり、それを書くのが著しく難しくなります。完璧なメモリフットプリントを最適化し、メモリをクリアするために処理を中断しないようにしているなら、それは素晴らしい勝利です。しかし、任意の瞬間の遅延を気にしないコンパイラのようなものを構築している場合、ガベージコレクションを行うために休憩を取るのは実際にはかなり良いことであり、はるかにシンプルで貪欲な方法でコードを書くことができます。
ガベージコレクションは多くのことに適しています。特にこのユースケースには非常に適していると思います。Webサーバーのようなものでは害を及ぼす可能性があります。なぜなら、GCを実行している間にユーザーがリクエストを行うと、そのリクエストは他の場合よりもはるかに長い時間がかかるからです。クレイジーな遅延スパイクが発生します。しかし、プロセスが複数秒かかる場合で、GCにヒットしても、誰が気にするでしょうか?
Rustはこれに対して素晴らしい選択です。TypeScriptチームのリーダーからの簡単な考えをご紹介します。Ryanさんこれがすべてリリースされたときにすぐに投稿してくれてありがとうございます。「私たちがGoを選んだとき、なぜRustや他の言語を選ばなかったのか疑問に思う人がいることは確かに知っていました。」
面白いことに、TypeScriptの作成者であるAndre Selbergは、Cも作成したことをご存知ないかもしれませんが、彼らはそれも探索しました。しかし彼らの結論は、Goが複数のシステムで動作するという移植性のために正しい選択だということです。明らかにGoのWindows対応は歴史的にそれほど良くなかったですが、Microsoftがそれを修正することは間違いないでしょう。すべてのデモでWindows使用していたので納得です。私が遇して問題は、Windowsではなくマックを使っているからかもしれません。
また、MSDチャンネルも登録しておきたいと思います。こういった素晴らしい内容を投稿するなら、注視しておきたいです。彼らはこれをRustで機能させるために多くのオプションを探索しましたが、それらすべてはパフォーマンスと人間工学の間で受け入れられないトレードオフがあるか、独自のガベージコレクション戦略を開発するというクレイジーな書き方になってしまいました。そのいくつかは近づきましたが、多くの安全でないコードになり、Rustには彼らが必要とするような人間工学的で柔軟なことを行うためのプリミティブがあるようには見えませんでした。
このように言われるとかなり予想通りです。ほとんどの言語はJSやTypeScriptからの移植を容易にすることを優先していません。最終的に、我々には2つの選択肢があります。Rustで完全に一からの書き直しを行い、それには何年もかかる可能性があり、誰も実際に使えないTypeScriptの互換性のないバージョンになるか、あるいはGoで移植を行い、1年以内に使えるものを得るかです。ご理解いただければ幸いです。
このジョークも大好きです。「これをRustで行うことの明確なメリットは、『なぜRustを選ばなかったのか』という質問にこれほど多く対応する必要がないことだけかもしれない。」Rust派の人たちをけなすつもりはありませんが、信じてください、私はそうするのが好きです。これらの質問がどれだけ多いか見てみましょう:「Microsoftがなぜかrustを選ばなかった」「なぜRustを選ばなかったと思いますか」「もうすぐ皆に『なぜRust?』と聞かれると思います」「なぜGoではなくRustを選ばなかったのか」「Rustはチャットルームに入ってきました」「でもなぜRustじゃないの?Goはかっこいいけど、そうすれば100倍速くなる。」
いやいや、全然そうじゃありません。ここでRustの並列処理を機能させるだけでも悲惨なことになるでしょう。Rustの同時実行レイヤーは自分で対処するために残されています。Tokyoがこれほど人気がある理由があります。私はこの話題に深入りしすぎました。動画全体が「なぜRustじゃないのか」という専用のものになるつもりはありませんでした。おそらく近々専用の動画を作るでしょう。
Goの最大のファンではありませんが、Goチームへの不満と支持の両方を表明したかっただけです。このプロジェクトには正しい選択でした。彼らがこれを選んだことに興奮しています。それがここまで速く進んでいることに興奮しています。そして、予定されているリリース日を共有することに非常に興奮しています。彼らは2025年の中頃までに、コマンドラインでインストールして実行できるTSCのネイティブ実装の完全なプレビューを用意する予定です。また、今年末までにフルプロジェクトビルドと言語サービスが機能することも期待しています。
これは年末までにVS Codeで使用可能になることを意味します。ただし、今すぐにでもできます。私のような素人でもこれを理解できるので、それほど難しくないはずです。セットアップの方法についてはリポジトリに説明があります。すでにLSPとして実行することもできます。録画前にこれをセットアップする時間がなかったので、興味がある方は確認してみてください。本当に素晴らしそうです。VS Codeでデバッグオプションを使用してトリガーするのが大好きです。素晴らしいことです。
ただし、最大の問題点があります。これが私がテストするのをはるかに難しくしている理由ですが、JSXはまだ準備ができていません。JSXはReactコードの中で小さなReactコンポーネントのようなHTML要素を記述するために使用される構文です。これはまだサポートされていません。多くのものがサポートされていますが、ほとんどすべての私のコードベースは何らかの形でJSXを使用しているため、まだ私のコードベースでこれを実行してパフォーマンスの向上を確認することはできません。
彼らはまた、ロードマップの計画を確認しました。TypeScript 6.0は、TypeScriptのJS版とGo版を整合させるために、いくつかの非推奨化と破壊的変更を導入し、TypeScript V7では正式にすべてがデフォルトでGoに移行します。これを展開するための素晴らしい計画です。彼らは巨大なTypeScriptコードベースを持つすべての人がこれらの勝利から恩恵を受けられるようにしたいと考えており、それは理にかなっています。
TypeScriptの目標は、最初からJavaScriptをMicrosoftのような規模の企業やコードベースにスケールさせることでした。Microsoftが JavaScript コードを書こうとしたとき、多くの開発者が多くのファイルや場所にコントリビュートする際に、それを機能させ続けることは絶対的に困難でした。TypeScriptはこの問題を解決するためにAndersによって書かれました。それをとてもうまく解決しましたが、新しい問題を導入しました。それは、この巨大なコードベースがある場合、タイプチェッカーのパフォーマンスが急激に低下することです。
今、彼らはそれを解決し、再びTypeScriptは、素晴らしい開発者体験を提供しながら、すべてのプラットフォームで動作する大企業が書いた巨大なアプリケーションのための解決策となっています。TypeScriptチームは歴史的に開発者体験を最優先にしてきました。そして、彼らがその開発者体験を本当に向上させるためにこれは正しい道だと思います。
最後の考えです。これは、私が最近多く考えていることであり、ここにも当てはまると思う点です。開発ツールの未来についてです。「Reactは最後のフレームワーク」という、すでに録画済みの動画がまもなく公開される予定です。この仮説は、既存のデータで学習したAIツールが今あるということです。ReactやTypeScriptのようなものに関するデータはとても多いため、新しいフレームワークや言語を導入するのは難しいことになります。なぜなら、AIがそれらの新しいものを書くのがあまり得意ではなくなるからです。単に例が少ないからです。
これは他のフレームワークや言語だけでなく、ReactやTypeScriptにも影響します。私が見ているのは、ReactとTypeScriptが大きな新しいAPIを簡単に追加できなくなってきているということです。なぜなら、私たちがすべてのコードを書くために使用しているAIで参照されている、既存の教育資料や既存のコードベースがそれらの機能について知っている可能性がほとんどゼロだからです。
だからこそ、既存のReact構文を取り、より速いコードを生成しようとするReactコンパイラが登場しました。また、構文をまったく変更せずに大きな恩恵をもたらすTypeScript Goコンパイラが登場した理由でもあります。私は、私たちの開発ツールの未来はこれにますます似てくると思います。
Pythonの世界におけるUVのようなもので、Pythonの開発環境の構築と設定がはるかに速くなります。また、Pythonの世界におけるMojoのようなもので、Pythonに見えるコードを書くとはるかに高速なCにコンパイルされます。これらのツールは、ソフトウェアの構文とエルゴノミクスを変更する代わりに、既存の作業方法を改善するツールを手に入れることになるソフトウェアの行き先の例だと思います。Microsoftがこれを強力に採用しているのは素晴らしいことです。
AI世界にも他の多くの利点があります。特に、タイプチェックのパフォーマンスがこれほど向上すると、AIボットがコードを書く作業中にツールを使用してタイプをチェックするのが著しく簡単になります。以前なら30秒以上待つ必要があった場合、良くありません。今や3秒未満待つだけで良いなら、突然これらのツールは、AIが巨大なコードベースに変更を加え、その変更が正しいかどうかをチェックするのにますます役立つようになります。とても素晴らしいです。
このようなプロジェクトには、間違う可能性がたくさんありました。TypeScriptチームがそれらをすべて回避するのを見るのは素晴らしいことです。このプロジェクトにとても興奮しています。将来の開発ツールから期待すべきことの素晴らしい先例だと思います。Andersをはじめ、Danny Rosenwaser、そしてTypeScriptチーム全員に大きな感謝を捧げます。早期に私を招き入れ、私のフィードバックを受け入れ、すべてを実現してくれたことに感謝します。TypeScript開発者であることをこれまで以上に誇りに思い、あなたたちはそれを価値あるものにし続けています。
皆さんの意見をお聞かせください。次回まで、タイプチェックを続けてください。


コメント