JSXからjQueryを使ってDOMの情報を取得

jQueryなどのような静的型付けのことを何も考えていないサードパーティ製のライブラリをJSXから使えるか?

使えたので報告。

もっとハマるかと思ったけど意外と素直に書けた。mainの最初の1行と最後の2行で「jQueryで最初のpタグを取得してそのinnerTextを取得し、文字列として編集してからコンソール出力」をやっている。間の5行のlogは答えにたどり着く過程を残してある。

import "js.jsx";
class _Main {
    static function main(args : string[]) : void {
        var jQuery = js.global["$"] as function(:string):variant;

        log jQuery;
        log jQuery("p");
        log (jQuery("p") as __noconvert__ Map.<variant>);
        log (jQuery("p") as __noconvert__ Map.<variant>)["0"];
        log ((jQuery("p") as __noconvert__ Map.<variant>)["0"] as Map.<variant>)["innerText"];

        var value : string = ((jQuery("p") as __noconvert__ Map.<variant>)["0"] as Map.<variant>)["innerText"] as string;
        log value + "---" + value;
    }
}

実際に試してみたい人のために少し解説すると、これをmain.jsって名前でコンパイルして、下のようなHTMLから呼んでいる。実行するとコンソールに「hoge---hoge」と表示されるはずだ。Chromeで確認した。(追記: コードをきれいにしてgithubに置いといた https://github.com/nishio/learning_jsx/tree/master/use_jquery)

<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script src="main.js"></script>
<script>
  $(function(){
    _Main$main$AS([]);
  })
</script>

<p>
hoge

ハマったところは4つ。

コードと離れていて読みづらいのでコードを再掲:

        var jQuery = js.global["$"]
            as function(:string):variant;
        var value : string = (((
            jQuery("p") as Map.<variant>)
            ["0"] as Map.<variant>)
            ["innerText"] as string);

まず文字列を引数に取る関数の型はfunction(string):variantではなくfunction(:string):variantというところ。

2つめは「Map は ["console"] みたく [] 演算子必須です」 by @kazuho

3つ目はjQuery("p") as Array.はnullになるところ。これはArrayに変換しようとするがArrayではないので失敗してnullになるのかな。

で、__noconvert__を付けてみたのだが、そうするとjQuery("p") instanceof Array == falseなんでアサートが発生してデバッガが呼ばれてしまう。これが4つ目。Mapはキーの型がstring固定なのでjQueryみたいに数値で添字付けされている「Array風オブジェクト」をどうしたらいいんだ?としばし悩んだが、"0" == 0だったはずだからもしかしていけるんじゃね?と文字列の"0"でアクセスしてみたら問題なくいけた。めでたしめでたし。

というわけでjQueryなどのような静的型付けのことを何も考えていないサードパーティ製のライブラリとJSXを混ぜて使えるか?という疑問に関しては間違いなくYESだと言えるだろう。ここではvariantと明示的なキャストを使って型チェックを避けつつ進んだけど、たとえば:

https://github.com/jsx/JSX/blob/master/lib/js/js/web.jsx

みたいな形でnative class Hogehogeって宣言をして、Hogehogeがどういう型のメンバを持っているかをJSXに教えてやればすんなり行くはず。それはまた今度試す。

Google Closure Compilerだと、このnative class宣言みたいなextern宣言を使うか、それとも片付けされていないサードパーティライブラリを使っている関数をまるごと/** @suppress {checkType} */してでかい穴を開けるかしかないので、JSXがvariantやキャストという形で小さい抜け穴を作れるのはとても好感が持てる。

続編:JSXからjQueryを使ってDOMの情報を操作 その2