Dev日記

プログラミング学習のまとめなど

Kotlin-AltJSの初心者向けTips&インプレ

AltJS-Kotlin

Android公式言語採用で一気にメジャーになったKotlinですが、実は JavaScriptに変換できます。いつからかは知りませんが、かなり当初からできたはず(1.0より前からあったはず)。

しかしマイナーすぎてあまり使っている人を見たことがありません。ネットの記事も少なめ。

軽くですが触った範囲でのTips&を記述していきます。

Tips

最初にコツ&ハマりポイントを。あまり出回ってないと思いますので

イベントハンドラ

DOMのクリックイベント等をWindowにイベントハンドラを追加したいことはよくあると思います。こんなやつです

<script>
function onLinkClick(param) {
}
</script>
<a href="javascript:onLinkClick('PARAMETER')">LINK</a>

で、このonLinkClick相当をKotlinにしたい。コイツが地味にkotlinだと面倒、というより最初やり方がわかりませんでした。結論からいうと asDynamic() を使います

fun main(args: Array<String>) {
   window.asDynamic().onLinkClick = {param : String -> 
      //do anything
   }
}

しかもmain関数内。最初はグローバル関数として記述すると思ってました。

jsの感覚で window.onLinkClick = と書くとビルド通らないので注意してください。

console.log

コイツも地味にはまりました。 print ですね。

一番ハマったのが、 改行がくるまでログに出力されない ことです。

このため、 print 単体では出力されません。そのあとの println で一気に出力されます。

これ結構ハマったので注意してください。

もいっこ、Object詳細をログに出してくれません。以下のようなパラメータをjs/kotlinに渡してログ出力させると…?

<a href="javascript:onLinkClick({text: 'I love Kotlin!'})">LINK</a>

f:id:nexus1:20170803230930p:plain

上がconsole.log、下がkotlinのprintlnです。見ての通り、オブジェクトの詳細がわかりません

なので kotlinでも console.log を使ったほうがいい です。printを使うメリットが分かりませんでした。記法が少し短い程度でしょうか? 基本はconsole.logがいいと思います。

js連携

jsのコードと連携する場合、external で定義ファイルを記述します。Typescriptでいう、 .d.ts ですね。

ためしに以下のようなjs用をKotlinで使おうとすると…?

//myMathをモジュール的に使ってるイメージ
myMath = {}
//モジュール内関数
myMath.sin = function(degree) {
    return Math.sin(degree * Math.PI / 180.0)
}
//クラス
myMath.Point = function(x, y) {
    self = this;
    this.x = x;
    this.y = y;
    this.abs = function() {
        return Math.sqrt(self.x * self.x + self.y * self.y);
    }
}

Kotlin用の定義ファイルはこうなります。

//モジュールはclassで包む。もっといい方法あるかも
external class myMath {
    //myMath.sinはstatic関数相当なので、companion objectで記述
    companion object {
        fun sin(degree: Double) : Double
    }
    //内部クラスはそのまま。ただしコンストラクタは constructor に限定される模様
    class Point {
        constructor(x: Double, y: Double)
        fun abs() : Double
    }
}
//ためしに使う
fun main() {
   println(myMath.sin(90.0)) //1
   val p = myMath.Point(1.0, 2.0)
   println(p.abs());  //2.23...
}

こんな感じ。正直自分も把握しきってないです(触り始めたばかりですし)

適当に書いたあと出力JSファイルをみて、それが意図した結果になってるか確認してることが多いです。.d.ts があるならそれベースでコンバートするのが楽でしょう。

小数はDoubleで

個人的にですが、小数は全部Doubleで扱ったほうが楽でした。Floatだといちいち末尾に f が必要でちょっと面倒。またKotlinはこの辺結構厳格なので、Double/Floatが入り乱れるとキャストが面倒です。

その上、jsにコンバートされたら全部number です。Java変換なら意味ありますがjs変換ではDouble/Floatを使い分けるメリットがありません。そんなわけで記法が楽な Doubleに統一がベスト と判断しました。


IntとLongについてもほぼ同等です…が、こっちは気分で分けています。

ユーザIDなど64bitなものは明示的にLongにして、それ以外はIntとしてます。 普段からLongは逆に面倒 なので。

またLongはID程度でしか使わないので加減算などをしないんですよね。よってLongだから扱いが面倒ということはないです(時刻はDoubleで扱う)

こちらもコンバートされた結果はIntだろうがLongだろうが一緒のはず。

ここは地味にKotlinToJSで使いづらい部分ですね。Typescriptと違って整数と小数を分けれるのはいいと思うのですが、それ以上は過剰機能という印象です。

一致しないオブジェクトを渡したとき

たとえば次のような定義ファイルを記述したとします。

external class User {
   var id : Long
   var name : String
}

idとnameがあるユーザ、よくあるパターンですね。これに {name: "NAME"} というオブジェクト、idが不足してるオブジェクトを渡した場合…?

//htmlが、href='javascript:onClick({name: "NAME"})'

window.asDynamyc().onClick(user : User) {
   println(user.id) //undefined
   println(user.name) //NAME
}

と、undefinedになります。当然といえば当然ですが。このへんは型に厳しいKotlinといえど注意する必要があるので気をつけましょう。

定義ファイルを全部をNullableとして定義するのも手ですが、コードがかなりめんどくさくなるのがネックです。 このへんはバランスをみて、になるかと思います。

インプレッション

まだ軽く触り始めたばかりですが、インプレッションを

Cons

  • 型があるのはやはりいい。TypeScriptより強力!
  • 言語仕様はTypeScriptより組みやすい気がする
  • IntelliJが賢い。VSCodeより補完がきく印象
  • IntelliJが一つのJSファイルとして出力してくれるのでwebpack等を記述する必要がない。楽。
    • どうもmain関数がある部分をエントリーポイントとして出力する模様

Pros

  • HTML/JSとの連携が難しい。ここはTypeScriptに軍配があがる
  • 型定義ファイルがほとんどない。自作する覚悟が必要。
  • 標準クラスの設計がJava基準で、JSの標準APIとは相性が悪い
  • 規模が大きい場合フレームワークをどうするか。
    • React/Angular/Vueのどれとも相性悪そう
    • フルスクラッチでいく覚悟が必要かもしれない

個人的印象ですが、 Kotlin内で閉じれるならすごくいい。閉じれない(外部JSと連携必須)ならTypeScript ということろでしょうか。

もちろんKotlin用外部ライブラリも少ないです。Androidのものなんて当然使いまわせません。

それに加え規模が大きくなるとReactやAngularは使いたくなるでしょう…ここは鬼門ですね。 小規模なプロジェクト以外は導入が難しそうなのに、小規模なプロジェクトで厳しい型チェックをするメリットは薄いというジレンマ。主力SPA系フレームワークとの相性悪そうなのは痛い。かろうじてAngular?

ちょっと面白いので趣味開発はありですが、業務採用となるとかなりハードルたかそうです。

こんな記事を書いておいてなんですが、個人的にはKotlinToJSよりELMのほうが面白いと思いますw