Slide 1

Slide 1 text

TypeScript コンパイラの 内側に片足入れる ryounasso

Slide 2

Slide 2 text

自己紹介 サイボウズ株式会社 kintone 新機能開発チーム SNS アカウント : @ryounasso TypeScript を触る機会 - 個人開発 (Next.js, React) - 業務 (React) 人生初 LT で緊張してます...

Slide 3

Slide 3 text

このスライドで目指すところ 1. ざっくりと処理の流れがわかる 2. それぞれの処理で、メインで動く関数とファイルがわかる TypeScript へのコントリビュートの足がかりに...!! > Good First Issue Issues · microsoft/TypeScript · GitHub 25件ほどあった

Slide 4

Slide 4 text

TypeScript はそのまま動かない TypeScript はブラウザや Node.js など JavaScript の実行環境で動作させるために JavaScript にコンパイルする必要がある TypeScript のコンパイラのソースは src/compiler フォルダ以下にある > https://github.com/Microsoft/TypeScript/tree/main/src/compiler 主要なファイルは以下の通り - scanner.ts - parser.ts - binder.ts - checker.ts - emitter.ts

Slide 5

Slide 5 text

大まかな流れ 字句解析 構文解析 識別子の 結合 型 チェック トランス パイル ソース ファイル 出力 AST の作成

Slide 6

Slide 6 text

- Abstract Syntax Tree (抽象構文木) のこと - コードをパースして、ソースコードを意味的に表す木構造のこと - 端的にいうとコードを表すオブジェクト AST とは 字句解析 構文解析 識別子の 結合 型 チェック トランス パイル AST の作成

Slide 7

Slide 7 text

字句解析 (scanner.ts) ● 字句解析を行う ● ソースコードをトークンに分割する。 ● scan がメインの関数 ● Scanner は createScanner で生成される。 ● scan を実行すると、スキャン中の位置や現在のトークンの詳細などを更新す る ● Parser によって内部的に制御される ● parser.ts でシングルトンの scanner が生成される (生成コスト削減) 字句解析 構文解析 識別子の 結合 型 チェック トランス パイル AST の作成

Slide 8

Slide 8 text

字句解析 (scanner.ts) // Sample usage initializeState(` var foo = 123; `.trim()); // Start the scanning var token = scanner.scan(); while (token != ts.SyntaxKind.EndOfFileToken) { let currentToken = ts.formatSyntaxKind(token); let tokenStart = scanner.getStartPos(); token = scanner.scan(); let tokenEnd = scanner.getStartPos(); console.log(currentToken, tokenStart, tokenEnd); } VarKeyword 0 3 Identifier 3 7 FirstAssignment 7 9 FirstLiteralToken 9 13 SemicolonToken 13 14 字句解析 構文解析 識別子の 結合 型 チェック トランス パイル AST の作成

Slide 9

Slide 9 text

構文解析 (parser.ts) ● 構文解析を行う ● createSourceFile がメインの関数 ● createParser で生成される シングルトンとして実装されている 主にやっていることは、 1. ソースコードをトークンに分割 (scanner) 2. 文法規則に従って、AST を作成する 字句解析 構文解析 識別子の 結合 型 チェック トランス パイル AST の作成

Slide 10

Slide 10 text

var sourceCode = ` var foo = 123; `.trim(); var sourceFile = ts.createSourceFile('foo.ts', sourceCode, ts.ScriptTarget.ES5, true); ----- /** createSourceFileの中で **/ result = Parser.parseSourceFile(fileName, sourceText, languageVersion, /*syntaxCursor*/ undefined, setParentNodes, ScriptKind.JSON, noop, jsDocParsingMode); ----- printAllChildren(sourceFile); SourceFile 0 14 ---- SyntaxList 0 14 -------- VariableStatement 0 14 ------------ VariableDeclarationList 0 13 ---------------- VarKeyword 0 3 ---------------- SyntaxList 3 13 -------------------- VariableDeclaration 3 13 ------------------------ Identifier 3 7 ------------------------ FirstAssignment 7 9 ------------------------ FirstLiteralToken 9 13 ------------ SemicolonToken 13 14 ---- EndOfFileToken 14 14 構文解析 (parser.ts) 字句解析 構文解析 識別子の 結合 型 チェック トランス パイル AST の作成

Slide 11

Slide 11 text

識別子の結合 (bind.ts) ● AST を読み取り、変数・関数などの宣言や定義を発見し、 代入先となる識別子と代入される値をバインドする ● bindSourceFile がメインの関数 ● createBinder で生成される 主にやっていることは 1. AST の巡回 2. シンボルの作成 ソースコードの識別子 (変数や関数名など)に関する情報を持つオブジェクト 3. スコープの解決 4. 型情報の収集 字句解析 構文解析 識別子の 結合 型 チェック トランス パイル AST の作成

Slide 12

Slide 12 text

識別子の結合 (bind.ts) const msg: string = "Hello, world"; welcome(msg); function welcome(str: string) { console.log(str) } Global Scope msg declared line 0 flags: BlockSopedVariable welcome declared line 3 flags: Function welcome Function Scope parent Global Scope str declared line 3 flags: BlockScopedVariable 字句解析 構文解析 識別子の 結合 型 チェック トランス パイル AST の作成

Slide 13

Slide 13 text

型チェック (checker.ts) tsc の中枢 コードの量がすごい...! (Github 上では多すぎて表示されなかった...) バインディングされた情報を元に、変数の型推論、型の整合性のチェック、 型のアサーションの確認などを行う。 checkSourceFile がメインの関数 字句解析 構文解析 識別子の 結合 型 チェック トランス パイル AST の作成

Slide 14

Slide 14 text

型チェック (checker.ts) const msg: string = "Hello, world"; (簡略化した AST) SourceFile └─ VariableStatement ├─ constKeyword └─ DeclarationList ├─ msg ├─ ColonToken ├─ StringKeyword ├─ EqualsToken └─ StringLiteral └─ "Hello World" 字句解析 構文解析 識別子の 結合 型 チェック トランス パイル AST の作成

Slide 15

Slide 15 text

型チェック (checker.ts) const msg: string = "Hello, world"; (簡略化した AST) SourceFile └─ VariableStatement ├─ constKeyword └─ DeclarationList ├─ msg ├─ ColonToken ├─ StringKeyword ├─ EqualsToken └─ StringLiteral └─ "Hello World" 字句解析 構文解析 識別子の 結合 型 チェック トランス パイル AST の作成

Slide 16

Slide 16 text

トランスパイル (emitter.ts) 最後に、TypeScript コードを JavaScript コードにトランスパイルする emitFiles がメインの関数 JavaScript はバージョンによって使える文法が大きく変わる。 トランスパイルのために、transformer を使用 src/compiler/transformers フォルダ以下に、 それぞれES版に対するトランスフォーマー等が存在している 流れは以下の通り 1. getTransformers で適用する transformer の配列を取得 2. emitFiles で JavaScript コードにトランスパイル 字句解析 構文解析 識別子の 結合 型 チェック トランス パイル AST の作成

Slide 17

Slide 17 text

トランスパイル (emitter.ts) JavaScript では型情報が不要になる (transformers) const msg: string = "Hello, world"; SourceFile └─ VariableStatement ├─ constKeyword └─ DeclarationList ├─ msg ├─ ColonToken ├─ StringKeyword ├─ EqualsToken └─ StringLiteral └─ "Hello World" const msg: string = "Hello, world"; SourceFile └─ VariableStatement ├─ constKeyword └─ DeclarationList ├─ msg ├─ EqualsToken └─ StringLiteral └─ "Hello World" 字句解析 構文解析 識別子の 結合 型 チェック トランス パイル AST の作成

Slide 18

Slide 18 text

まとめ 字句解析 scanner.ts scan 構文解析 parser.ts createSourceFile 識別子の結合 bind.ts bindSourceFile ソース ファイル 出力 型チェック checker.ts checkSourceFile トランスパイル emitter.ts emitFiles

Slide 19

Slide 19 text

参考記事 - TypeScriptコンパイラの内側 - TypeScript Deep Dive 日本語版 (gitbook.io) - https://www.youtube.com/watch?v=X8k_4tZ16qU&list=PLYUbsZda9oHu- EiIdekbAzNO0-pUM5Iqj&index=5 - https://qiita.com/eloy/items/9d0d8227121a1dcbdf71 - https://github.com/microsoft/TypeScript