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コンパイラにおける`const`

Posted at

TypeScriptコンパイラにおけるconst

今回、const のコンパイルにおける特有な要素を紹介していきたいと思う。

まず、constの基本から始めよう:constとは再代入できない変数宣言である。これはTypeScriptもJavaScriptも変わらない。

しかし、constの再代入エラーの際に一つ注意すべき点がある。JavaScriptはインタプリタ型言語なので、再代入エラーは実行時に起き、例外処理の一環としてthrowされる。

以下はMDNのドキュメントで使われる例:

const number = 42;

try {
  number = 99;
} catch (err) {
  console.log(err);
  // 型エラー: const `number'への無効な代入
}

console.log(number);
// 出力: 42

これに対し、TypeScriptはコンパイル型であるが故に実行前にエラーを報告する。実行前というと、VS Code ならば、エラーの赤い波線が挙げられる。

ちなみに、constlet宣言の処理は非常に似ているため、発生し得るエラー以外ほとんど何も変わらない。要は、かなり短い記事になる。

構文解析

ここでバインド以外の処理段階におけるconstletとの唯一の違いを紹介する。

変数宣言のコンパイル手順をより詳しく知りたい方はぜひ前回の記事を参照してください。

AST生成

AST生成にて、parser.ts でparseVariableDeclarationListで const のフラグをつけ、「こいつは const だよ」と伝える。

switch (token()) {
    ...
    case SyntaxKind.ConstKeyword:
        flags |= NodeFlags.Const;
        break;
    ...
}

constの仕組み

バインド

バインドを担当する binder.ts にはsymbolTable(シンボルテーブル)というグローバル変数がある。
このテーブルは、ソースに現れるあらゆるシンボル(変数名・関数名・クラス名・型名・モジュール名など)を保管した上で、あるシンボルに対する全ての宣言も記録する。

バインド段階をより詳しく理解するためにdeclareSymbol関数を見ていきましょう。

declareSymbol関数の働きを次のようにまとめることができる:

  1. まず、現在評価している宣言における識別子はすでにシンボルテーブルにが存在しているかどうか判明
  2. なければ、新しいシンボルを生成
  3. 同じブロックに既存の宣言があれば、重複宣言エラーを報告
  4. シンボルに今回の宣言を追加
  5. シンボルを返す

これでconst宣言と値を結合することができた。
今度はconstならではのエラーを紹介する。

再代入エラー

よほど純粋的なプログラミングを用いない限り、定数宣言において最もの簡単に起きるエラーは再代入エラーだろう:

const x = 0;
...
x = 1;	// 再代入エラー

再代入エラーは型チェック中に起きる。checkIdentifier(checker.ts) で識別子を確かめる際、定数宣言にあたると判明される。

// const は読み取り専用データの一種
if (isReadonlySymbol(localOrExportSymbol)) {
    // この読み取り専用データは変数ならば、const だと断言できる
    if (localOrExportSymbol.flags & SymbolFlags.Variable) {
        error(node, Diagnostics.Cannot_assign_to_0_because_it_is_a_constant, symbolToString(symbol));
    }
    ...

右下のDiagnostics.Cannot_assign_to_0_because_it_is_a_constantが報告された、const 文法の誤用をチェックする。

未初期化エラー

const x; // あれ?

checkGrammarVariableDeclaration(checker.ts)でconst変数がちゃんと初期化されたか確認される。

else if (!node.initializer) {
  ...
  case NodeFlags.Const:
    return grammarErrorOnNode(node, Diagnostics._0_declarations_must_be_initialized, "const");
  ...
}

以上、const特有のエラー紹介だった。

まとめ

ご覧いただいたように、constletのコンパイル手順はほぼ等しい。
値の代入の際にconst特有のエラーも、実は二つしかないということだ。

参考資料

直接TypeScriptコンパイラのソースを参照したい方は以下のURLをご利用ください:

  • parser.ts:constフラグ
  • binder.ts:declareSymbol関数
  • checker.ts:残念ながら、このファイルは5万行に及ぶせいでGitHubにて表示できない。ソースをご覧になりたい方はTypeScriptプロジェクトのGet Startedセクションを参照し、TypeScriptをクローンしてください。
    • git clone--depth=1をつけるとダウンロード時間は大幅に減るのでぜひ、このオプションを使ってください。
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?