tkawachi Blog

Rails で TypeScript を動かそうとして失敗した記録

Microsoft 発の型付き JavaScript こと TypeScript が人気ですね。 typescript-rails という gem があったので、動くのか試してみました。

結論から言っておくと、他のスクリプトやライブラリを参照する TypeScript をコンパイルすることが今回はできませんでした。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ rails new typescript-rails-test -T
$ echo 'gem "typescript-rails"' >> Gemfile
$ bundle
$ bundle exec rails g controller test index
      create  app/controllers/test_controller.rb
       route  get "test/index"
      invoke  erb
      create    app/views/test
      create    app/views/test/index.html.erb
      invoke  helper
      create    app/helpers/test_helper.rb
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/test.js.coffee
      invoke    scss
      create      app/assets/stylesheets/test.css.scss
$ rm app/assets/javascripts/test.js.coffee
$ echo 'alert("Hello TypeScript")' > app/assets/javascripts/test.js.ts
$ bundle exec rails s

これで http://localhost:3000/test/index にアクセスすると、正しく alert が表示される。 いいぞ。

次に JQuery が使ってみたい。

app/assets/javascripts/test.js.ts
1
2
3
$(function() {
  alert("document ready")
})

うーむ。エラーがおきた。

1
2
SyntaxError: (4,1): The name '$' does not exist in the current scope
  (in /Users/kawachi/gitworks/typescript-rails-test/app/assets/javascripts/test.js.ts)

Issue #5 に報告されている問題と同一。 /// <reference path="jquery.d.ts" /> という行をいれて $ の存在を TypeScript compiler に知らしめねばならないらしいが、これが今のところできないようだ。

https://github.com/TimothyKlim/typescript-ruby/issues/1 によれば、compiler 内の IO class が利用している CommonJS の機能を ExecJS が隠してしまっているのが原因とのこと。 ExecJS を経由せずに直接 Node.js を叩けばいいのでは、と。

これが ExecJS から node を叩くときにソースコードをラッピングするものだが、 よく見ると module, exports, require, console が undefined にされていることがわかる。(result = program(); のところで引数に何も渡されていない) 隠されている CommonJS の機能とはきっとこのことを言っているのだろう。

execjs/support/node_runner.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
(function(program, execJS) { execJS(program) })(function(module, exports, require, console) { #{source}
}, function(program) {
  var output, print = function(string) {
    process.stdout.write('' + string);
  };
  try {
    result = program();
    if (typeof result == 'undefined' && result !== null) {
      print('["ok"]');
    } else {
      try {
        print(JSON.stringify(['ok', result]));
      } catch (err) {
        print('["err"]');
      }
    }
  } catch (err) {
    print(JSON.stringify(['err', '' + err]));
  }
});

このへん をみると ExecJS の作者は、副作用を与えるこれらの機能を ExecJS に取り込むつもりは毛頭ないようだ。

上のコードを手元で少し書き換えて module, exports, require を書き潰さないようにしてみたが、やはり同じエラーが出た。 ということは、これらの関数が undefined になっていることが直接の理由ではないということか。

JavaScript 処理系 Rhino で TypeScript コンパイラのビルドを試してみた によると TypeScript がサポートしているのは Node.js と JScript だけなので、Node.js の存在を前提として TypeScript compiler を動かすモジュール作ったほうが早そうな感じ。

時間が取れたら作りたいな。

Comments