Slide 1

Slide 1 text

TypeScriptの パフォーマンス改善 2024/05/11 @TSKaigi-トラック2-LT 🗣️やじはむ

Slide 2

Slide 2 text

自己紹介 やじはむ / yajihum ・株式会社SasaeLのフロントエンドエンジニア  ・5月からCOMPASSに在籍しながら出向中  ・公教育で使われる先生向けの校務支援プロダクトを開発中  ・フロントエンドエンジニア募集中です! ✊ ・エンジニア3年目 ・X,GitHub: @yajihum

Slide 3

Slide 3 text

TypeScriptの公式リポジトリに PerformanceについてのWikiがある

Slide 4

Slide 4 text

今日話すこと 1. TypeScriptのパフォーマンスとは何か、   そしてなぜそれが重要なのか 2. コンパイル速度を改善するための具体的な手法と その背後にある理論

Slide 5

Slide 5 text

今日話すこと 1. TypeScriptのパフォーマンスとは何か、   そしてなぜそれが重要なのか 2. コンパイル速度を改善するための具体的な手法と その背後にある理論

Slide 6

Slide 6 text

TypeScriptのパフォーマンスとは何か 1. コンパイル速度 TypeScriptからJavaScriptへの変換速度のこと コンパイル速度を上げることで、tscによる型チェックや 単純なビルド、テストの実行時間などを削減できる

Slide 7

Slide 7 text

例えばこの記事では 参照:https://zenn.dev/knowledgework/articles/speedup-typecheck

Slide 8

Slide 8 text

記事から引用: 改善前の TypeScript ファイルは自動生成されたコードを含めると 約 29 万行あり、 tsc の実行に 50 秒ほどかかっていました

Slide 9

Slide 9 text

TypeScriptのパフォーマンスとは何か 2. 編集体験 VSCodeなどのIDEやテキストエディタで使用されている tsserverによる型チェックやエラー表示、コード補完などの フィードバックを得る体験のこと

Slide 10

Slide 10 text

例えばこの記事では 参照:https://zenn.dev/knowledgework/articles/speedup-typecheck

Slide 11

Slide 11 text

記事から引用: ある日、新たに関数の実装を追加しようとしたその時、 tsserver(TypeScript の language server)による補完候補が なかなか表示されないことに気づいたのです。

Slide 12

Slide 12 text

つまり、TypeScriptのパフォーマンス改善をすることで 開発体験や開発効率を上げることができる

Slide 13

Slide 13 text

パフォーマンスを改善する方法はいくつかあるが、 今回は「コンパイルしやすいコード」にするための方法を見ていく

Slide 14

Slide 14 text

今日話すこと 1. TypeScriptのパフォーマンスとは何か、   そしてなぜそれが重要なのか 2. コンパイル速度を改善するための具体的な手法と その背後にある理論

Slide 15

Slide 15 text

1. 型宣言にはtypeよりもinterfaceを使う 参照:https://github.com/microsoft/TypeScript/wiki/Performance#preferring-interfaces-over-intersections

Slide 16

Slide 16 text

「複数のオブジェクト型の合成に関しては インタフェースを使うといい」ということが明記されている インターフェース(interface)の場合 型エイリアス(type)の場合 &を使って交差型(type aliases to intersections)を作る

Slide 17

Slide 17 text

なぜか? インターフェース: 交差型: 1. 同じプロパティがあった場合はそれを検出し、 単一のフラットなオブジェクトを生成する(プロパテ ィのオーバーライド) →無効な型があった場合はエラーを発生させる 2. エラーの可能性が排除されたフラットな オブジェクトのプロパティに対してのみ型チェックが 行われる 3. 型関係がキャッシュされる 1. ただ各プロパティを再帰的にマージするだけで、 never型が生成されることもある 2. マージ結果がnever型などの無効な型であっても エラーは発生せず、型チェックは続行される 3. 型関係がキャッシュされない

Slide 18

Slide 18 text

補足:Linterを使った制御 typescript-eslintでは「consistent-type-definitions」という interfaceかtypeに強制するルールがあったりする

Slide 19

Slide 19 text

2. 型注釈を使用する 型注釈をつけることで型推論によって行われる匿名型の解釈よりも 宣言ファイルの読み書きにかかる時間を減らすことができる 型注釈とは以下の「:number」ようなもの

Slide 20

Slide 20 text

型注釈なし 例を見てみる npm run tsc importが挿入されている

Slide 21

Slide 21 text

コンパイルの流れ 1. 参照したい型が現在のスコープ内で直接アクセス可能かどうかを確認 (同じファイル内で定義された型やインポートされた型など) 2. アクセス可能でない場合、他のモジュールからインポートできるかどうかを確認する 3. インポートできる場合、該当のファイルをインポートするための最も適切なパスを 計算する 4. 型参照を表現するための新しいノード(ASTの一部)を生成する。これは型注釈としての その型を使用するためのコードとなる 5. 生成された型参照ノードを出力して生成されたコード(型注釈)を 実際のTypeScriptファイルに書き出す

Slide 22

Slide 22 text

型注釈をつけた場合 コンパイル前から参照先がわかっている 型注釈がない場合のインポート先を探す一連の流れが省かれる 💡

Slide 23

Slide 23 text

ただし... 型推論はとても便利な機能なので全てのコードに対して 明示的に型注釈を付ける必要はない コードに遅い部分があった場合は型注釈をつけることを試してみるでOK

Slide 24

Slide 24 text

補足:Linterを使った制御 typescript-eslintでは「explicit-function-return-type」という 関数とクラスメソッドに明示的な戻り値の追加を強制するルールがあったりする

Slide 25

Slide 25 text

3. 複雑な型は名前付けをする 例えば以下のような複数の条件分岐によって 戻り値の型が定義されている関数を考える ・関数fooが実行されるたびに戻り値の条件型を検証する必要がある ・SomeType型の複数のインスタンスを比較する際にfooの戻り値の型構造を  再度検証する必要がある

Slide 26

Slide 26 text

別で名前付けをする 型エイリアス(type)を使用して別でFooResult型を定義 一度型の検証が行われるとコンパイラはキャッシュし、 もし同じ型が出現した際にはキャッシュを利用できる

Slide 27

Slide 27 text

まとめ ・TypeScriptのパフォーマンスを向上させることで  開発体験や開発効率を上げることが出来る ・コンパイル速度を改善する方法として以下がある  1. 型宣言にはtypeよりもinterfaceを使う 2. 型注釈を使用する 3. 複雑な型は名前付けをする

Slide 28

Slide 28 text

今回話した内容について書いた記事がブログにあります https://blog.yajihum.dev/blog/posts/tech/20240218_tips_for_speeding_up_typescript こちらの方がより詳しく書いてあるため良かったら参考にしてもらえると幸いです!