1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

TypeScript のコンパイラーの処理の流れ

Last updated at Posted at 2022-11-13

はじめに

最近、typescript のコンパイラーのコードをサラサラーっと読んでいたのですが、なんとなく区切りがついたので、簡単にまとめておこうと思います。いつも通りではありますが、この記事で間違ったことを書く可能性は大いにあるため、正確な情報が必要であれば、参考に記載している参照元 URL を確認してください。

参考

typescript のコンパイルに関するソースコードがあるディレクトリです。
https://github.com/microsoft/TypeScript/tree/main/src/compiler

typescript の型チェックに関するソースコードです。超巨大なので typescript のリポジトリをクローンしてから、ローカルのエディターで開きましょう(汗)。
https://github.com/microsoft/TypeScript/blob/main/src/compiler/checker.ts

typescript のメンテナーがコンパイラーに関してまとめてくれています。
https://github.com/microsoft/TypeScript-Compiler-Notes

typescript のメンテナーがミニマムの Typescript を実装しています。
https://github.com/sandersn/mini-typescript

コンパイラーの流れ

ものすごくざっくりとコンパイラーの流れを分割すると、以下の3つになります。

  • テキスト解析(parsing text)
  • 型チェック(Type Checking)
  • コード出力(Emit)

各工程について記載していきます。

テキスト解析(parsing text)

テキスト解析の最終的な目的はシンタックスツリーを作成することです(シンタックスツリーについては後述)。
シンタックスツリーを作成するために、まずは、コンパイル対象のコードをテキストとして読み取り、キーワード毎に分割していき、分割されたキーワードに対して名称を与えます。
例えば、以下のコードをキーワード毎に分割し、名称を与えると...

function hello() {
  console.log("Hi");
}

以下になります。

// 分割対象`名称`
function`FunctionKeyword`
 `WhitespaceTrivia`
hello`Identifier`
(`OpenParenToken`
)`CloseParenToken`
 `WhitespaceTrivia`
{`OpenBraceToken`
`NewLineTrivia`
 `WhitespaceTrivia`
console`Identifier`
.`DotToken`
log`Identifier`
(`OpenParenToken`
"Hi"`StringLiteral`
)`CloseParenToken`
;`SemicolonToken`
`NewLineTrivia`
}`CloseBraceToken`
`NewLineTrivia`
`EndOfFileToken`

上記の例を見ていくと、関数の宣言は FunctionKeyword と言う名称が与えられ、関数名の hello は Identifier になっています。また、console も Identifier です。javascript の構文にない hello や console は固有の名称ということで Identifier になります。

これらの処理は、typescript の playground に Ts Scanner というプラグインを入れることでも確認できます。
https://www.typescriptlang.org/play?#code/GYVwdgxgLglg9mABACwKYBt1wBQEpEDeAUIohAgM5zqoB0WA5tgEQASMzuA3EQL5A

スクリーンショット 2022-11-13 18.17.52.png

次は、これらに階層の情報を付与され、シンタックスツリーが作成されます(ここで作成されるシンタックスツリーが超重要で、この後の処理でもずっと使われます)。
シンタックスツリーの全量を書くのはしんどいので、省略しますが、ざっくり以下になります。

SourceFile:
  - statements: [
    Function Declaration
      - name: Identifier
      - body: Block
        statements: [
          ExpressionStatement
            expression: CallExpress
              ...
        ]
  ]

ソースファイルがルートにあり、その一階層下に関数宣言(Function Declaration)があり、関数宣言の一階層下に、関数名を表す name(Identifier)があります。こんな感じでシンタックスツリーが作成されています。

※ 本当は、関数宣言(Function Declaration)に name 以外にも色々な属性が付与されていますが、上記のざっくりしたシンタックスツリーでは、属性は省略して記載しています。属性とは、例えば、関数宣言の開始位置、終了位置があり、これらは vscode 等のエディターでエラー箇所をハイライトするために使われたり...他にも色々と属性はありますが、これらは後続の処理で使用されます。

これらの処理は、Typescript の playground の設定から AST Viewer を有効することでも確認できます。

スクリーンショット 2022-11-13 18.40.21.png

型チェック(Type Checking)

型チェックは、宣言された "もの" に対して実行されていきます。"もの" は、変数や関数、インターフェイスなどです。
"もの" はテキスト解析でシンタックスツリーとして表現されており、シンタックスツリーの頭からつま先まで、型チェックは行われます。

<!-- これらを頭からつま先まで型チェック -->
SourceFile:
  - statements: [
    Function Declaration
      - name: Identifier
      - body: Block
        statements: [
          ExpressionStatement
            expression: CallExpress
              ...
        ]
  ]

例えば、以下のコードがあります。

function hello() {
  console.log("Hi");
}

上記に宣言された関数は、シンタックスツリーでは、hello(Identifier)と言う名前の関数(Function Declaration)で表現されており、型チェックでは、hello(Identifier)と言う名前の関数(Function Declaration)が重複して存在しないかがチェックされます。もし、存在すれば Duplicate function implementation. と言うエラーが得られます。

次に、以下のコードがあります。

const num: number = 'A'
// error message : Type 'string' is not assignable to type 'number'.

このコードは、Type 'string' is not assignable to type 'number'. というエラーが得られます。
このコードのシンタックスツリーを見ると以下のようになっています(関連のある属性のみ表示)。

VariableDeclaration:
    - initializer: StringLiteral
    - type: NumberKeyword

VariableDeclarationnum という名前の変数を宣言していることを意味しており、この変数の初期値(initializer)は A という StringLiteral になっていますが、型(type)は NumberKeyword となっています。
型チェックにて、初期値(initializer)と型(type)が整合していないため、先の Type 'string' is not assignable to type 'number'.というエラーメッセージを作成します。

コード出力(Emit)

.js, .d.ts, .map ファイルが出力されます。コード出力では、テキスト解析で得られたシンタックスツリーを元に、tsconfig に合わせて新しいシンタックスツリーを作成します。例えば、tsconfig のターゲットが es2015 であれば、ESNext, ES2020, ES2019, ES2018, ES2017, ES2016, ES2015 のシンタックスは変換される必要があります。

まとめ

かなり粒度は荒くしていますが、簡単にコンパイラーの処理の流れのメモとして置いておきます。以上終わり。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?