/jvm2dts

Converts Java/Kotlin model classes to TypeScript definitions for client-side type safety

Primary LanguageJava

jvm2dts

Generates TypeScript definitions (d.ts) for Java/Kotlin model classes using reflection/asm that can be used for client-side type checking of API calls and responses.

All public getters are read by default, in a similar way to Jackson. Works with Java/Lombok/Kotlin data classes. Java records are not yet supported.

Usage

Using jvm2dts requires that you include class paths of both the target project and jvm2dts itself. Java version must be at least 11

Example: java -classpath path/to/package jvm2dts.Main -exclude MyRegExp -cast MyClass=number,AnotherClass=string package1 package2 package3
Usage: <main class> [options]
  Options:
    -c, -cast
      Comma-separated key=value map to make classnames matching the key into 
      specified value
    -classesDir
      Recursively look for classes from a location
    -d, -data-only
      Find only Kotlin data classes
      Default: false
    -e, -exclude
      Excludes classes in the generation matching a Java RegExp pattern
    -excludeDir
      Comma-separated list to filter out package names when using classesDir

Read more about setting class paths in Java

Converted TypeScript interfaces output from stdout and all errors are through stderr

Using in Gradle (Kotlin DSL)

Release

Note: unreleased versions can also be obtained from https://jitpack.io/#codeborne/jvm2dts

val jvm2dts by configurations.creating

repositories {
  mavenCentral()
  maven { url = uri("https://jitpack.io") }
}

dependencies {
  jvm2dts("com.github.codeborne:jvm2dts:VERSION")
}

tasks.register("types.ts") { 
  dependsOn("classes")
  doLast {
      val mainSource = sourceSets.main.get()
      project.file("api/types.ts").writeText(ByteArrayOutputStream().use { out ->
        project.javaexec {
          standardOutput = out
          mainClass.set("jvm2dts.Main")
          jvmArgs = listOf("--add-exports=java.base/jdk.internal.org.objectweb.asm=ALL-UNNAMED") // Java 16+ needs this
          classpath = jvm2dts + sourceSets.main.get().runtimeClasspath
          args("-exclude", ".*SuffixOfClassNameIDontWant|PrefixOfClassNameIDontWant.*", 
               "-cast", "MyNumericClass=number",
               "-classesDir", "${project.buildDir}/classes/kotlin/main")
        }
      out.toString()
    })
  }
}

tasks.withType<KotlinCompile> { // or JavaCompile
  finalizedBy("types.ts")
}

Or depend directly on the source code (e.g. if Jitpack is down), add this to settings.gradle:

sourceControl {
  gitRepository(java.net.URI("https://github.com/codeborne/jvm2dts.git")) {
    producesModule("com.github.codeborne:jvm2dts")
  }
}

Then you can depend on a tagged version number, which Gradle will clone and build during building of your project.

Recursive directory class loading

While jvm2dts can recursively walk through directories with -classesDir parameter, this is not the suggested mode of operation, as it might be necessary to maintain an exclusion list (-excludeDir parameter) against internal packages in the project. It is also susceptible to Kotlin's internals which may change over time.

Most common use of this will be inside Gradle's build directories in a project:

// Gradle Kotlin DSL, Kotlin
"-classesDir ${project.buildDir}/classes/kotlin/main"
// Gradle Groovy, Java
"-classessDir " + project.buildDir + "/classes/java/main"

Enums

Because TypeScript reads definition files only at compile-time and discards them on runtime, enums inside *.d.ts will always be undefined - if you have enums, it is suggested to write into a *.ts file instead

Nullability

jvm2dts can read Nullable annotations and will append ? to the name of a variable.

jvm2dts can read JsonProperty annotations and uses reflection to obtain the value()

Similarly, getters annotated with JsonIgnore will be omitted.