LoginSignup
11
10

More than 5 years have passed since last update.

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

Posted at

showtype演算子

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

showtype.ts
console.log(showtype (n => n*2));
showtype.js
console.log("(n: any) => number");

演算子の優先順位はtypeofやdeleteと同じとします。

パーサーとレキサーを変更する

DeleteKeywordの下にShowtypeKeywordを追加します。

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演算子が存在するかのようにコンパイルされるようになります。

$ jake
$ node built/local/tc.js showtype.ts
$ cat showtype.js
showtype.js
console.log(showtype (function (n) { return n * 2; }));

showtypeの結果を文字列型にする

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

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;
showtype.ts
var type = showtype 123;
type = 456;

ソースを書き換えたら再びビルドしてテスト。

$ jake
$ node built/local/tc.js showtype.ts
showtype.ts(2,1): Type 'number' is not assignable to type 'string'.

想定通りエラーが出るようになりました。

ジェネレーターを変更する

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

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));
                 }

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

$ jake
$ node built/local/tc.js showtype.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);
showtype.js
console.log("(n: any) => number");
console.log("{ key: string[]; id: number; }");
console.log("null");
console.log("any");

まとめ

新しいコンパイラはシンプルになっていいですね!安定版が待ち遠しいです。
フォークしたリポジトリ

11
10
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
11
10