JavaScriptバインディングの書き方 Level 2

@kazuhoさんの[JavaScriptバインディングの書き方](http://d.hatena.ne.jp/kazuhooku/20131203/1386038668)に書かれてない、JavaScriptバインディング上級編です。Java/C++的な思想できちんと作られたクラスであれば問題ないのですが、JavaScriptのあふれんばかりのフリーダムを駆使したコードだと、ただnativeを付けたクラスを書くだけではうまく対応できなかったり、キャストしまくらないとダメだったりします。

# 返り値がオブジェクト型

[esprima](http://esprima.org/)というよくできたECMAScriptのパーサのライブラリのJSX版を作った時に遭遇した事象です。返り値が木構造のASTツリーなんですが、JSONというか、オブジェクトそのまま、という感じでした。もちろん、JSXでJSONは扱えますが、プロパティ名の短縮表記(`json.member`)は使えず、カッコで文字列でキーを指定した上で、asで型を明示しないとダメです。当然、コードも無駄に長く、読みにくくなります。

推奨する書き方は短く、危険だったりあまり使ってほしくない書き方は長く、というのは言語設計の中でよく行われる意思決定ですが、JSXではきちんと型を書くことでコードが綺麗になるという思想の元で作られています(要出典)。

この場合は、返り値で使われるJSONの型を定義し、nativeのメソッドからそれを返すようにすれば、`result.member` 形式で書けるようになるし、タイプミスも発見できるようになるので、ちょっと苦労してでもやる価値はあります。フリーダムに作られるJSONを分析して、「きっとこのタイプはこの属性しか持たないはずだ」という情報を元にクラスを定義していくのは、[物によっては大変です](https://github.com/shibukawa/esprima.jsx/blob/master/lib/esprima.jsx)が・・・

書き方は、他のクラスと同じように書けばいいのですが、 `native __fake__` を付けます。newはできないけど、コンパイル時に型チェックを行えるクラスというのが作れます。 `final` も付けるべきですかね？

```js:esprima.jsxの一部
native __fake__ class EsprimaBlockToken
{
    var type : string;
    var body : EsprimaToken[];
}

native __fake__ class EsprimaProgramToken extends EsprimaBlockToken
{
    var comments : EsprimaCommentToken[];
    var tokens : EsprimaSimpleToken[];
}

native class esprima {
    static function parse(src : string) : EsprimaProgramToken;
    static function parse(src : string, option : Map.<boolean>) : EsprimaProgramToken;
    static function parse(src : string, option : EsprimaOption) : EsprimaProgramToken;
} = "require('esprima')";
```

一点課題もあって、メンバー名に使えないプロパティ名を使いたい時どうするの？という問題です。元々のJSONを使うJSで簡易表現ができないなどの弊害があるので、元のコードの素性が悪いといえばそれまでなんですが・・・esprima.jsxでも、演算子の種類(operatorというキーの値)が取れないのは既知の問題です。

# JSXで使いたいインタフェースに合わせてJS側を改造

nativeのクラス宣言の後ろに  `= "require('module')";` と書くと、外部からロードするモジュールが扱えるようになる、というのは@kazuho先生のエントリーにもありますが、じつはここには何でもかけちゃいます。

Redisのnode.jsライブラリのJSXラッパーでは、[既存のRedisClientクラスにメソッドを追加](https://github.com/shibukawa/redis.jsx/blob/master/lib/redis.jsx#L26)しています。[node-redis](https://github.com/mranney/node_redis)は、Redisの2ワードコマンド(script loadとか)は、1ワード目がメソッド名、2ワード目を最初の引数に、というルールになっていますが、2ワード目によってメソッドのシグニチャがぜんぜん違うので、コンパイラの型チェックとの相性はとても良くないです。redis.jsxでは、2ワードメソッド(scriptloadとか)を個別にメソッド化することで、nativeラッパーを作成しやすくしています。

```js:redis.jsxの簡略版
native class redis {
    static function createClient() : RedisClient;
} = '''
require('redis');

(function () {
    var RedisClient = require('redis').RedisClient;
    var twoWordsCommands = ['script exists', 'script kill', 'script flush', 'script load'];

    twoWordsCommands.forEach(function (command) {
        RedisClient.prototype[command.split(' ').join('')] = function () {
            // 新しい実装
        };
    });
})();
''';

native __fake__ class RedisClient {
    // 追加したメソッド
    function scriptexists(script : string, callback : (Error, int[]) -> void) : boolean;
    function scriptflush(callback : () -> void) : boolean;
    function scriptkill(callback : () -> void) : boolean;
    function scriptload(script : string, callback : (Error, string) -> void) : boolean;
    // 既存のメソッドも当然書けます。
    function get(key : string, callback : (Error, string) -> void) : boolean;
}
```

独立した関数として提供されているライブラリを、native構文の中でオブジェクトのメンバーとしてまとめて、「それぞれの関数はクラスのstatic関数である」という感じのインタフェースを与える、ということもできます (むしろそっちの方が多いかも)。