TypeScriptを魔改造してshowtype演算子を追加する

# showtype演算子
showtype演算子とは、**コンパイル時**にオペランドの型名をに文字列に変換する演算子です。Haxeにも**$type**という似たような関数があるので、そちらを真似して作ってみました。ただし、$type関数は型名をエラー出力するのに対し、showtype演算子は単に文字列化するだけです。

```ts:showtype.ts
console.log(showtype (n => n*2));
```

```js:showtype.js
console.log("(n: any) => number");
```
演算子の優先順位はtypeofやdeleteと同じとします。



# パーサーとレキサーを変更する
`DeleteKeyword`の下に`ShowtypeKeyword`を追加します。

```diff
diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts
index d3e40a2..cf30c7a 100644
--- a/src/compiler/parser.ts
+++ b/src/compiler/parser.ts
@@ -1467,6 +1467,7 @@ module ts {
                 case SyntaxKind.TildeToken:
                 case SyntaxKind.ExclamationToken:
                 case SyntaxKind.DeleteKeyword:
+                case SyntaxKind.ShowtypeKeyword:
                 case SyntaxKind.TypeOfKeyword:
                 case SyntaxKind.VoidKeyword:
                 case SyntaxKind.PlusPlusToken:
@@ -1876,6 +1877,7 @@ module ts {
                 case SyntaxKind.TildeToken:
                 case SyntaxKind.ExclamationToken:
                 case SyntaxKind.DeleteKeyword:
+                case SyntaxKind.ShowtypeKeyword:
                 case SyntaxKind.TypeOfKeyword:
                 case SyntaxKind.VoidKeyword:
                 case SyntaxKind.PlusPlusToken:
diff --git a/src/compiler/scanner.ts b/src/compiler/scanner.ts
index 2ff3217..18e5aae 100644
--- a/src/compiler/scanner.ts
+++ b/src/compiler/scanner.ts
@@ -72,6 +72,7 @@ module ts {
         "require": SyntaxKind.RequireKeyword,
         "return": SyntaxKind.ReturnKeyword,
         "set": SyntaxKind.SetKeyword,
+        "showtype": SyntaxKind.ShowtypeKeyword,
         "static": SyntaxKind.StaticKeyword,
         "string": SyntaxKind.StringKeyword,
         "super": SyntaxKind.SuperKeyword,
diff --git a/src/compiler/types.ts b/src/compiler/types.ts
index 6b84676..dd5eccd 100644
--- a/src/compiler/types.ts
+++ b/src/compiler/types.ts
@@ -80,6 +80,7 @@ module ts {
         DebuggerKeyword,
         DefaultKeyword,
         DeleteKeyword,
+        ShowtypeKeyword,
         DoKeyword,
         ElseKeyword,
         EnumKeyword,
 ```

これで、あたかもshowtype演算子が存在するかのようにコンパイルされるようになります。

```bash
$ jake
$ node built/local/tc.js showtype.ts
$ cat showtype.js
```

```js:showtype.js
console.log(showtype (function (n) { return n * 2; }));
```

# showtypeの結果を文字列型にする
showtype演算子の結果の型は未定義なので、内部ではunknown型として扱われ、最終的にはany型になります。これを文字列型として扱われるように変更します。

```diff
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 004a4f3..4814361 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -4228,6 +4228,7 @@ module ts {
                 case SyntaxKind.DeleteKeyword:
                     return booleanType;
                 case SyntaxKind.TypeOfKeyword:
+                case SyntaxKind.ShowtypeKeyword:
                     return stringType;
                 case SyntaxKind.VoidKeyword:
                     return undefinedType;
```

```ts:showtype.ts
var type = showtype 123;
type = 456;
```
ソースを書き換えたら再びビルドしてテスト。

```bash
$ jake
$ node built/local/tc.js showtype.ts
showtype.ts(2,1): Type 'number' is not assignable to type 'string'.
```
想定通りエラーが出るようになりました。


# ジェネレーターを変更する
いよいよJSを生成する部分を変更します。ジェネレーターからは型チェックのAPIが見えないので、ノードに直接文字列を持たせることにしました。

```diff
diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index 4814361..8deb69d 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -4219,6 +4219,10 @@ module ts {
 
         function checkPrefixExpression(node: UnaryExpression): Type {
             var operandType = checkExpression(node.operand);
+            if (node.operator === SyntaxKind.ShowtypeKeyword) {
+                (<any>node).typeName = typeToString(operandType);
+            }
+
             switch (node.operator) {
                 case SyntaxKind.PlusToken:
                 case SyntaxKind.MinusToken:
diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts
index d1e2367..91eb2e3 100644
--- a/src/compiler/emitter.ts
+++ b/src/compiler/emitter.ts
@@ -781,6 +781,11 @@ module ts {
             }
 
             function emitUnaryExpression(node: UnaryExpression) {
+                if (node.operator === SyntaxKind.ShowtypeKeyword) {
+                    write(JSON.stringify((<any>node).typeName));
+                    return;
+                }
+
                 if (node.kind === SyntaxKind.PrefixOperator) {
                     write(tokenToString(node.operator));
                 }
```


ビルドしてテスト。
ちゃんと動いているようです。

```bash
$ jake
$ node built/local/tc.js showtype.ts
```

```ts:showtype.ts
console.log(showtype (n => n * 2));
console.log(showtype { key: ["value"], id: 1 });
console.log(showtype null);
console.log(showtype <any>null);
```

```js:showtype.js
console.log("(n: any) => number");
console.log("{ key: string[]; id: number; }");
console.log("null");
console.log("any");
```


# まとめ
新しいコンパイラはシンプルになっていいですね！安定版が待ち遠しいです。
[フォークしたリポジトリ](https://github.com/norahiko/TypeScript)