* こちらはオプトテクノロジーズ社内勉強会での発表資料になります
TypeScript リポジトリの歩き方
TypeScript のコードリーディングのはじめの一歩としてどこから手を付ければ良いのかを知る手助けになればと思いこさえました
目次
- コードベースの概観についてドキュメントから知る
- 実装のエントリポイントから読み進める
- 動かして試す
- 自分が実際にコードリーディングしたときの例
コードベースの概観についてドキュメントから知る
まず TypeScript の内部アーキテクチャの概観について知っていきましょう
TypeScript の wiki が大変参考になります
- Architectural Overview: コード上のコンポーネントを図示しつつ説明している Layer Overview や全体で利用されているデータ構造を説明する Data Structures といった、実装を理解する上で最も基礎となる説明が記載されている。必読
- Coding guidelines: コーディング規約
- Compiler Internals: Compiler API の内部的な実装上の思想などがコンポーネントごとに記載されている。ただし空の項目も多い
コードベースの概観についてドキュメントから知る
本家のドキュメント以外の資料
-
TypeScript Deep Dive: TypeScript Compiler Internals: TypeScript Deep DiveでのコンパイラAPI解説。コンパイラAPIの処理の流れや用語などの解説があります
- 日本語版はこちら
実装のエントリポイントから読み進める
TypeScriptはエンドポイントとして以下の2つがあります
tsc.ts
正味、 tsc.ts
の ts.executeCommandLine(ts.sys, ts.noop, ts.sys.args);
から順を追って読んでいくだけ、という説明になっちゃいます
もうちょい細かい話は過去に TypeScript の tsc コマンドを叩いたときに const x:number = ''; がコンパイルエラーになるまでの道のり という記事を書いたのでこちらを読んでいただければと
server.ts
tsc.tsと違って若干行数が多いファイルですが、 tsserverは標準入出力によってエディタとやりとりをする
ということを知っていれば、以下の行で require("readline");
しているところを起点に読み進められるでしょう
readlineインスタンスの .on
メソッド呼び出しを探していけばOKです
動かして試す
動かして試すと一口に言っても動かし方は色々あります
- デバッガを使って tsc/tsserver を動かす
- ユニットテストを動かす
- AST Viewer を使って Node の構造を確かめる
- Compiler API を使って Node や checker の挙動を確かめる
デバッガを使って tsc/tsserver を動かす
デバッグする方法もこれまた色々あります
本家リポジトリのwikiの Debugging TypeScript
という項目内に様々なデバッグ方法が記載されているので参照すると良いでしょう
自分は過去にtsserverをデバッグした際は素朴に node --inspect ./built/local/tsserver.js
を実行してデバッガでアタッチするという方法を取りました(やり方について記事も書いているので参考までに tsserverをデバッグする)
余談ですが、最近は VSCodeにAuto Attach Feature という機能が搭載されていて、わざわざデバッグ設定を作らなくてもVSCodeのターミナルから node --inspect
を起動すると勝手にプロセスにアタッチしてくれたりします。便利
ユニットテストを動かす
まあ普通にユニットテストを動かして挙動を見てみるというだけです
ユニットテストを指定して動かす方法やユニットテストをデバッグする方法などもREADMEとかで紹介されています
AST Viewer を使って Node の構造を確かめる
みんな大好きAST Viewer
TypeScriptの Node
のツリー構造をビジュアライズしてくれるサービスです
めちゃくちゃ便利
外部的な振る舞いを見るものではあるので、内部実装を読む時よりはCompilerAPIを使ってなにかやるときに便利なものではありますが、内部のコードベースで表しているSymbolやらがどういう構造なのかを試しながら挙動を確かめるという点で有用でしょう
Compiler API を使って Node や checker の挙動を確かめる
CompilerAPIによって実装されてる機能の外部的振る舞いを試してみるというアプローチです
まあ正直ここまで来ると内部実装を試すにはだいぶ迂遠な感じはします
checker.tsの挙動だけ個別に試す、というように個別のAPIを部分的に調べるにはCompilerAPIを使うほうが手っ取り早いかも?
コードリーディングのデモ
自分が以前修正したtsserverの不具合を題材にしてデモをしてみようと思います
どんな不具合?
エディタ上で指定した変数を参照してる箇所一覧を表示しようとすると、特定条件下において参照が全く表示されなかったという問題です
VSCodeだとshift + F12で出せるやつですね
以下のようなファイル構成のときに、 mod.ts
にて、定義されている変数 a
をindex.tsで参照してはずなのに、どこからも参照されていないように見えていたという状態でした
// index.ts
import { a, b, c } from './mod';
console.log(a, b, c);
// mod.ts
export const [a, b] = [1, 2];
export const c = 3;
本来こういう感じに index.ts
の参照が取れて欲しいの図
コードリーディングのアプローチ
(前提) tsserverの応答に本来入ってるべき index.ts
の参照がないことを切り分け済みとする
- tsserverの
"command":"references"
コマンドを探し、その実装の実体を追う - tsserverは標準入力からコマンドを実行できるので、デバッガを立ち上げつつ、不具合が発生するようなコマンド(先述の例で言えば
mod.ts
の変数a
について"command":"references"
コマンド)を叩き、挙動を確かめる
デモ
終わりに
というわけでTypeScriptリポジトリの歩き方を紹介しました
自分はTypeScriptの実装を今まで何度か読んできているのですが、これだけ巨大なリポジトリでもかなり読みやすいと感じています
実装で用いられている用語や概念に統一感・一貫性があるので、お決まりさえわかっていれば案外読めちゃうな、という感覚です
もちろんいくら一貫性を保ってコードが書かれているからといって、型システムの根幹のような難解な部分はありますが・・・
また、巨大なコードベースを読むということが結構楽しいし糧になるというのも個人的にオススメしたいポイントです
巨大になってもちゃんと開発を進められるようなコードベースの一例として、という感じなのでこれはTypeScriptに限ったことでは全然ない話ですが