ts2kt を Gradle で実行する

概要

TypeScript の型定義を Kotlin ファイルに変換する Node.js スクリプトである ts2kt を Gradle で実行できるようにします。

2017/8/31 現在、ts2kt は使える状態ではありません。 生成された Kotlin ファイルでは多数のエラーが発生します。

目次

確認環境

  • IntelliJ IDEA Community 2017.2
  • Kotlin 1.1.4-2
  • Gradle 3.5
    • Groovy 2.4.10

参考情報

解説

例として Vue.js の型情報 (*.d.ts) を Kotlin ファイル (*.kt) に変換する。

手順としては以下の通り。

  1. node をプロジェクト用にダウンロード
  2. npm を使い ts2kt と vue をインストール
  3. ts2kt で *.d.ts*.kt に変換

まずは build.gradle を以下の通りに記述する。

buildscript {
    ext.gradle_node_version = '1.2.0'

    repositories {
        jcenter()
        maven {
            // gradle-node-plugin
            url "https://plugins.gradle.org/m2/"
        }
    }

    dependencies {
        classpath "com.moowork.gradle:gradle-node-plugin:$gradle_node_version"
    }
}

apply plugin: 'com.moowork.node'

node {
    // node をプロジェクト用にダウンロード
    download = true
}

task ts2kt(type: NodeTask) {
    String nodeDir = "${project.projectDir}/node_modules"
    script = file("$nodeDir/ts2kt/ts2kt.js")

    String destDir = 'src/main/kotlin/org/vuejs'
    file(destDir).mkdir()

    args = ['-d', destDir] + file("$nodeDir/vue/types").listFiles().collect { it.absolutePath }
}

npm の設定として pakage.json を以下の通りに記述する。

{
  "name": "Vue.kt",
  "version": "2.4.2",
  "description": "Vue.kt that is Vue.js in Kotlin",
  "devDependencies": {
    "ts2kt": "0.0.14",
    "vue": "2.4.2"
  }
}

設定が完了したら以下のコマンドを実行する。

$ ./gradlew npmInstall ts2kt

src/main/kotlin/org/vuejs ディレクトリに Kotlin ファイルが生成される。

例えば vue.kt の一部。

external interface `T$0` {
    @nativeInvoke
    operator fun invoke(): VNode
    @nativeInvoke
    operator fun invoke(tag: String, children: VNodeChildren): VNode
    @nativeInvoke
    operator fun invoke(tag: String, data: VNodeData? = definedExternally /* null */, children: VNodeChildren? = definedExternally /* null */): VNode
    @nativeInvoke
    operator fun invoke(tag: Component, children: VNodeChildren): VNode
    @nativeInvoke
    operator fun invoke(tag: Component, data: VNodeData? = definedExternally /* null */, children: VNodeChildren? = definedExternally /* null */): VNode
    @nativeInvoke
    operator fun invoke(tag: AsyncComponent, children: VNodeChildren): VNode
    @nativeInvoke
    operator fun invoke(tag: AsyncComponent, data: VNodeData? = definedExternally /* null */, children: VNodeChildren? = definedExternally /* null */): VNode
}
... <snip> ...
    open fun <T> `$watch`(expOrFn: (this: Vue /* this */) -> T, callback: WatchHandler<Vue /* this */, T>, options: WatchOptions? = definedExternally /* null */): () -> Unit = definedExternally
... <snip> ...

元は vue.d.ts のこの部分。

export type CreateElement = {
  // empty node
  (): VNode;

  // element or component name
  (tag: string, children: VNodeChildren): VNode;
  (tag: string, data?: VNodeData, children?: VNodeChildren): VNode;

  // component constructor or options
  (tag: Component, children: VNodeChildren): VNode;
  (tag: Component, data?: VNodeData, children?: VNodeChildren): VNode;

  // async component
  (tag: AsyncComponent, children: VNodeChildren): VNode;
  (tag: AsyncComponent, data?: VNodeData, children?: VNodeChildren): VNode;
}
... <snip> ...
  $watch<T>(
    expOrFn: (this: this) => T,
    callback: WatchHandler<this, T>,
    options?: WatchOptions
  ): (() => void);
... <snip> ...

生成された Kotlin ファイルにはいくつか問題がある。

  1. T$0 という名前が同一パッケージ内で複数定義されることになり名前が衝突する。
  2. @nativeInvoke は Deprecated
  3. VNodeChildren, Component, AsyncComponent などの型が定義できていない
    • Union Type に対応できていないため型を定義できない
  4. パラメータ名に this (Kotlin のキーワード) が使われている

Union Type に関する議論はされている様子。

discuss.kotlinlang.org

既存の JavaScript ライブラリを使うのであれば ts2kt による型情報を補足が欲しいところ。