ひだまりソケットは壊れない

ソフトウェア開発に関する話を書きます。 最近は主に Android アプリ、Windows アプリ (UWP アプリ)、Java 関係です。

まじめなことを書くつもりでやっています。 適当なことは 「一角獣は夜に啼く」 に書いています。

Kyoto.js #1 で 『TypeScript 言語処理系ことはじめ』 という発表をしました

2012 年 11 月 15 日に開催された Kyoto.js #1 で 『TypeScript 言語処理系ことはじめ』 という発表をしました。

発表内容

資料は SlideShare にアップロードしました。

概要

  • 前半は JavaScript に代わる言語を使用したいという動機の話、あるいは 「JavaScript 最高!!」 という話
  • 後半は TypeScript の言語処理系を (tsc コマンドからではなく) 直接 JavaScript で触る話

TypeScript の言語の基本的な話はしてないです

TypeScript の言語機能だとか構文の話は基本的にしてません。 まあ JavaScript の勉強会ですしね!

TypeScript の基本的なことは公式ページをご覧ください。

ECMAScript って何なの?

JavaScript の勉強会に来るような人はみんな知っているかもしれませんが、ECMAScript ってのは JavaScript の言語機能のコア部分を標準化して作られた言語です。 詳細は以下のページを見るとよいでしょう。

2012 年 11 月 27 日現在、ECMAScript の最新のバージョンは 5.1 で、最近のブラウザは基本的に ECMAScript 5.1 をサポートしていると思います。 ECMAScript 5.1 にはクラス定義の構文などはないのですが、現在策定作業が進んでいる ECMAScript の次のバージョン (ES.next とか ECMAScript 6 とか呼ばれてるもの) にはクラス定義の構文などが含まれる予定になっています。 草案は定期的に公開されていて下記ページで見ることができます。

TypeScript と ECMAScript

TypeScript の構文は ECMAScript 5 の構文のスーパーセットになっています。

TypeScript syntax is a superset of Ecmascript 5 (ES5) syntax.

TypeScript Language Specification 0.8 (PDF)

つまり、ECMAScript 5 の構文で書かれた JavaScript のコードは、全て TypeScript (0.8) の構文としても正しい、ということだと思います *1。 また、TypeScript のクラス定義の構文などは ECMAScript 6 の草案をベースにしているようです。

そういった点を考えると、すでに存在している JavaScript のプロジェクトを、少しずつ TypeScript に書き換えていく、というような移行が (他の JavaScript を置き換えるような言語と比べて) やりやすいと思います。

TypeScript 言語処理系を JavaScript から使用する

TypeScript のコンパイルの方法としても最も一般的 *2 なのは tsc コマンドを使用して、node 上で TypeScript 言語処理系を動かしてコンパイルさせることだと思います。 ただ、この方法は実際にやってみるとコンパイルごとに結構時間がかかってしまいます (私の環境だと小さな TypeScript ファイルの 1 回のコンパイルに 3, 4 秒程度かかりました)。 これは、コンパイルの処理そのものに時間がかかっているのではなくて、コンパイラの準備にかかるオーバーヘッドが大きいことが原因でしょう。

こういう問題を回避するために、コンパイラの準備を一度したらそのプロセスをずっと留めておいて毎回同じプロセスを使ってコンパイルさせる、というようなことをしたいわけですが、tsc コマンドを使っているだけではあまり細かな制御ができません *3。 そこで、TypeScript 言語処理系の実体である JavaScript を直接触ってコンパイルしたりしてみましょう、という話。

TypeScript 言語処理系の実体は、TypeScript プロジェクト の中の bin/typescript.js というファイルです *4。 これを読み込むと TypeScript.TypeScriptCompiler というコンストラクタが使えるようになるので、以下のような JavaScript を書いて TypeScript のソースコードをコンパイルできます。

// 出力用オブジェクトの定義
var outfile = {
	source: "",
	Write: function (s) {
		this.source += s;
	},
	WriteLine: function (s) {
		this.source += s + "\n";
	},
	Close: function () {}
}

// コンパイラ生成
var compiler = new TypeScript.TypeScriptCompiler(outfile);

// エラーの扱い
compiler.parser.errorRecovery = true;
compiler.setErrorCallback(function (start, len, message, block) {
	console.log("error : " + message);
});

// TypeScript のソースコード追加 (これがコンパイルされる)
var src1 = "class Test { aaa: string; };\n var ttt = 100;";
compiler.addUnit(src1, 'test1.ts');
var src2 = "///<reference path='test1.ts' />\nvar test = new Test(); test.";
compiler.addUnit(src2, 'test2.ts');

// 型チェック
compiler.typeCheck();

// 実行
// 以下は TypeScript 0.8.0 用に第 1 引数に bool 値を渡していますが
// 0.8.1 では以下の false は消す必要があります
compiler.emit(false, function createFile(fileName) {
	console.log("create file : " + fileName);
	return outfile;
});

console.log('compiled: ' + outfile.source);
console.dir(compiler); // TypeScriptCompiler オブジェクトの中を見ると抽象構文木が見える

コード補完させたい

VimEmacs で TypeScript を書くことを考えると、記述中の TypeScript のソースコードの指定した位置における識別子等のコード補完をさせるための機能が欲しいところです。 そのための機能は TypeScript のプロジェクトの中に含まれています。

詳細は以前記事を書いたのでそちらを参照ください。

そういえば何やら Microsoft の人が TypeScript のサービス用プロジェクトみたいなのを公開してましたが、あれはなんなんでしょうね。 「TypeScript Services Server」 という名前からすると、補完等の言語サービスを提供してくれるのかもしれないですね。 (ちゃんと見てないのでわかんない。)

Node 以外の環境で動かしたい

TypeScript 言語処理系は JavaScript で実装されていますが、現在 (version 0.8.1) のところサポートされているのは Windows の JScript 処理系 (?) と Node だけです。

さまざまな JavaScript 言語処理系で TypeScript コンパイラを動かすための障害として、ファイルの読み書きや標準入出力等の IO 機能が JavaScript 処理系間で統一されていないという問題があります。 TypeScript 言語処理系では、IO というオブジェクトの中に JavaScript 言語処理系固有の IO の機能を隠ぺいすることで、実装間の差異を吸収しています。 そのため、動かしたい JavaScript 処理系に対応した IO の機能を IO オブジェクトの中に記述すれば、その JavaScript 処理系で TypeScript 処理系を動かすことができる可能性があります。

詳細は以前書いた記事をご覧ください。

おわり

JavaScript 最高!!

*1:ただし、構文的には正しくてもコンパイルできるかどうかは別問題。 例えば型チェックが通らなくてコンパイルできない可能性もある。

*2:VisualStudio 上で使う場合を除く

*3:tsc コマンドには watch オプションというものがあって、ファイルの変更を検出して自動的にコンパイルし直したりしてくれるのですが、いまいち使いにくい。

*4:TypeScript 言語処理系自体は TypeScript で書かれていて、それをコンパイルしたものがこの JavaScript ファイルです。