🐥

Kotlin/JSでTypeScriptの型定義ファイルを生成する方法

2020/09/18に公開

Kotlin1.4からKotlin/JSのKotlinコードからTypeScriptの型定義ファイル(.d.ts)が生成されるようになったみたいなので、少し触ってみました。

この機能はIRコンパイラ専用の機能で、まだプレビュー版なので業務に使うにはまだ早そうです。
https://kotlinlang.org/docs/reference/js-ir-compiler.html#preview-generation-of-typescript-declaration-files-dts

型定義ファイルを生成機能を使う準備

build.gradle.ktsでIRコンパイラー使うように設定

  1. jsの第一引数にIRを指定
  2. binaries.executable() をkotlin内に記述
...
kotlin {
    js(IR) { // ①
        browser {
				}
				binaries.executable() // ②
		}
}

使い方

1. 型定義を生成したい物に @JsExport を付与

@JsExport はトップレベルの物にしかつけれないので注意

@JsExport
class TopLevelClass(val a: Int, var b: Int)

2. gradle build を実行

build/js/packages/{プロジェクト名}/kotlin/{プロジェクト名}.d.ts に型定義ファイルが生成されます。

いろいろ試してみた

top level class

Kotlinコード

@JsExport
class TopLevelClass(val a: Int, var b: Int)

.d.ts

export class TopLevelClass {
    constructor(a: number, b: number);
    readonly a: number;
    b: number;
}

extend class

Kotlinコード

@JsExport
open class SuperClass(val superA: Int)

@JsExport
class SubClass(val subA: Int): SuperClass(1)

.d.ts

export class SuperClass {
    constructor(superA: number);
    readonly superA: number;
}
export class SubClass extends SuperClass {
    constructor(subA: number);
    readonly subA: number;
}

※ 親クラスにも @JsExport をつけないと親クラスの型定義は出力されません。

top level data class

Kotlinコード

@JsExport
data class DataClass(val a: Int, var b: Int)

.d.ts

export class DataClass {
    constructor(a: number);
    readonly a: number;
    component1(): number;
    copy(a: number): DataClass;
    toString(): string;
    hashCode(): number;
    equals(other: Nullable<any>): boolean;
}

Nested Class

Kotlinコード

@JsExport
class OuterClass(val outerA: Int) {
    class NestedClass(val innerA: Int)
}

コンパイルエラー...

e: java.lang.IllegalStateException: Can't find name for declaration CLASS CLASS name:NestedClass modality:FINAL visibility:public superTypes:[kotlin.Any]

Inner Class

Kotlinコード

@JsExport
class OuterClass(val outerA: Int) {
    inner class InnerClass(val innerA: Int)
}

コンパイルエラー...

e: java.lang.IllegalStateException: Can't find name for declaration CLASS CLASS name:InnerClass modality:FINAL visibility:public [inner] superTypes:[kotlin.Any]

interface

Kotlinコード

@JsExport
interface Interface {
    fun method(a: Int): Int
    fun defaultMethod(a: Int): Int = a
    val field: Int
}

.d.ts

export interface Interface {
    method(a: number): number;
    defaultMethod(a: number): number;
    readonly field: number;
}

abstruct class

Kotlinコード

@JsExport
abstract class AbstractClass(val a: Int)

.d.ts

export interface Interface {
    method(a: number): number;
    defaultMethod(a: number): number;
    readonly field: number;
}

object

Kotlinコード

@JsExport
object Object {
    val a: Int = 1
}

コンパイルエラー...

e: java.lang.IllegalStateException: Class Object with kind: OBJECT

companion object

Kotlinコード

@JsExport
class A {
    companion object {
        fun method(): Int = 0
        val a: Int = 0
    }
}

コンパイルエラー...

e: java.lang.IllegalStateException: Class DataClass.Companion with kind: OBJECT

top level function

Kotlinコード

@JsExport
fun topLevelFunction(): Int = 0
export function topLevelFunction(): number;

Enum Class

Kotlinコード

@JsExport
enum class Enum {
    A, B, C
}

コンパイルエラー...
@JsExport はEnumにつけることができない

Declaration of such kind (enum class) cant be exported to JS

バグの状況などはyoutrackで管理されてます
https://youtrack.jetbrains.com/issues?q=%23{KJS: d.ts generation}&_ga=2.23992570.1134254226.1600398765-1301939789.1594112335

まとめ

まだプレビュー版という事もありバグは多いですが、型定義ファイルが生成できるようになったので、Kotlin/JSでライブラリを作るみたいなパターンは増えてきそうだと思いました。

Discussion