{"id":43336295,"url":"https://github.com/ntrrgc/ts-generator","last_synced_at":"2026-02-02T00:36:41.838Z","repository":{"id":15903225,"uuid":"78955496","full_name":"ntrrgc/ts-generator","owner":"ntrrgc","description":"Generate TypeScript definitions from Kotlin/Java/JVM classes","archived":false,"fork":false,"pushed_at":"2022-10-01T08:02:58.000Z","size":157,"stargazers_count":249,"open_issues_count":24,"forks_count":43,"subscribers_count":5,"default_branch":"master","last_synced_at":"2023-11-07T15:19:00.841Z","etag":null,"topics":["java","kotlin","typescript-definitions","typescript-generator"],"latest_commit_sha":null,"homepage":null,"language":"Kotlin","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ntrrgc.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-01-14T17:34:08.000Z","updated_at":"2023-10-27T16:28:50.000Z","dependencies_parsed_at":"2023-01-11T19:13:31.604Z","dependency_job_id":null,"html_url":"https://github.com/ntrrgc/ts-generator","commit_stats":null,"previous_names":[],"tags_count":5,"template":null,"template_full_name":null,"purl":"pkg:github/ntrrgc/ts-generator","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ntrrgc%2Fts-generator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ntrrgc%2Fts-generator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ntrrgc%2Fts-generator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ntrrgc%2Fts-generator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ntrrgc","download_url":"https://codeload.github.com/ntrrgc/ts-generator/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ntrrgc%2Fts-generator/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28997086,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-01T23:10:54.274Z","status":"ssl_error","status_checked_at":"2026-02-01T23:10:47.298Z","response_time":56,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["java","kotlin","typescript-definitions","typescript-generator"],"created_at":"2026-02-02T00:36:41.748Z","updated_at":"2026-02-02T00:36:41.829Z","avatar_url":"https://github.com/ntrrgc.png","language":"Kotlin","funding_links":[],"categories":[],"sub_categories":[],"readme":"# TypeScript definition generator for the JVM\n\nThis library generates TypeScript definitions that cover a set of Kotlin and Java classes using Kotlin reflection.\n\nTypeScript definitions are useful when data classes are serialized to JSON and\nhandled in a JavaScript or TypeScript web frontend as they enable context-aware type checking and autocompletion in a number of IDEs and editors.\n\nts-generator supports:\n * Primitive types, with or without explicit int.\n * Kotlin and Java classes.\n * Data classes.\n * Enums.\n * Any type.\n * Generic classes, without type erasure.\n * Generic constraints.\n * Class inheritance.\n * Abstract classes.\n * Lists as JS arrays.\n * Maps as JS objects.\n * Null safety, even inside composite types.\n * Java beans.\n * Mapping types.\n * Nullability annotations, when allowed by the retention policy.\n * Customizing class definitions via transformers.\n * Parenthesis optimization: They are placed only when they are needed to disambiguate.\n * Emitting either `null` or `undefined` for JVM nullable types.\n \n## Installation\n\nts-generator requires Kotlin 1.1. Kotlin 1.0 is not compatible as its reflection library is not powerful enough to do this transformation. \n\nThen you need to include this library in your project. The easiest way is to [download it from JitPack](https://jitpack.io/#ntrrgc/ts-generator). For instance, in Gradle you would add this to `build.gradle`:\n\n```groovy\nrepositories {\n    maven { url 'https://jitpack.io' }\n}\n\ndependencies {\n    compile 'com.github.ntrrgc:ts-generator:1.1.1'\n}\n```\n\n## Basic usage\n\nThe first you need is your Kotlin or Java classes or interfaces, for instance:\n\n```kotlin\nenum class Rarity(val abbreviation: String) {\n    Normal(\"N\"),\n    Rare(\"R\"),\n    SuperRare(\"SR\"),\n}\n\ndata class Card(\n    val ref: String,\n    val rarity: Rarity,\n    val name: String,\n    val description: String,\n    val command: String?,\n    val playCard: (() -\u003e Unit)?\n) {\n    val generatedTitleLine = \"*$name* [$rarity]\"\n}\n\ndata class Inventory(\n    val cards: List\u003cCard\u003e = listOf()\n)\n\ndata class Player(\n    val name: String,\n    val inventory: Inventory = Inventory(),\n    val achievementsProgress: List\u003cAchievementCompletionState\u003e = listOf(),\n    val notices: List\u003cNotice\u003e = listOf()\n)\n\ndata class Notice(\n    val dateTime: LocalDateTime,\n    val text: String\n)\n\ndata class Achievement(\n    val ref: String,\n    val title: String,\n    val description: String,\n    val measuredProperty: (player: Player) -\u003e Int,\n    val neededValue: Int\n)\n\ndata class AchievementCompletionState(\n    val achievementRef: String,\n    val reachedValue: Int\n)\n```\n\nThen use `TypeScriptGenerator` to generate the TypeScript definitions, like this:\n\n```kotlin\nfun main(args: Array\u003cString\u003e) {\n    println(TypeScriptGenerator(\n        rootClasses = setOf(\n            Player::class\n        ),\n        mappings = mapOf(\n            LocalDateTime::class to \"Date\",\n            LocalDate::class to \"Date\"\n        )\n    ).definitionsText)\n}\n```\n\nYou will get an output like this:\n\n```typescript\ninterface AchievementCompletionState {\n    achievementRef: string;\n    reachedValue: number;\n}\n\ntype Rarity = \"Normal\" | \"Rare\" | \"SuperRare\";\n\ninterface Card {\n    command: string | null;\n    description: string;\n    generatedTitleLine: string;\n    name: string;\n    rarity: Rarity;\n    ref: string;\n}\n\ninterface Inventory {\n    cards: Card[];\n}\n\ninterface Notice {\n    dateTime: Date;\n    text: string;\n}\n\ninterface Player {\n    achievementsProgress: AchievementCompletionState[];\n    inventory: Inventory;\n    name: string;\n    notices: Notice[];\n}\n```\n\nThen you can paste it into a `.d.ts` file and declare it in your environment, e.g:\n \n * TypeScript: Just add the file to your project.\n * JS with Visual Studio Code: Create a `jsconfig.json` in Visual Code and adding it in the `include` section of `typeAcquisition`. [You can read more about type adquisition in the VS Code documentation.](https://code.visualstudio.com/Docs/languages/javascript#_automatic-type-acquisition)\n * IntelliJ/WebStorm/PHPStorm: Open the Settings and look for *Libraries* inside *JavaScript*. Click *Add* and create a new library declaration adding the created definition file. [Read more about it in the documentation](https://www.jetbrains.com/help/idea/2016.3/configuring-javascript-libraries.html)\n  \n![](vscode_screenshot.png)\n\n## Advanced features\n\nThis generator can handle more complex data types. Some examples are shown below:\n\n### Mapping types\n\nSometimes you want to map certain Kotlin or Java classes to native JS types, like `Date`. \n\nThis can be done with the `mappings` argument of `TypeScriptGenerator`, as show in the first example. \n\nNote the types mapped with this feature are emitted as they were written without any further processing. This is intended to support native JS types not defined in the Kotlin or Java backend.\n\n### Int type\n\nCurrently TypeScript only supports one number type: `number`. \n\nThis may change if [a proposal for int types](https://github.com/Microsoft/TypeScript/issues/4639) succeeds. Also, some people may want to be extra explicit and do:\n\n```typescript\ntype int = number;\n```\n\nIn order to be able to document if a type may or may not be integer. In any case, you can instruct `TypeScriptGenerator` to use explicit `int` with the `intTypeName` parameter. For instance:\n \n```kotlin\nfun main(args: Array\u003cString\u003e) {\n    println(TypeScriptGenerator(\n        rootClasses = setOf(\n            AchievementCompletionState::class\n        ),\n        intTypeName = \"int\"\n    ).definitionsText)\n}\n```\n\nThe output will be:\n\n```typescript\ninterface AchievementCompletionState {\n    achievementRef: string;\n    reachedValue: int;\n}\n```\n\n### Inheritance support\n\n```kotlin\nopen class BaseClass(val a: Int)\n\nclass DerivedClass(val b: List\u003cString\u003e): BaseClass(4)\n\nfun main(args: Array\u003cString\u003e) {\n    println(TypeScriptGenerator(\n        rootClasses = setOf(\n            DerivedClass::class\n        )\n    ).definitionsText)\n}\n```\n\nThe output is:\n\n```typescript\ninterface BaseClass {\n    a: number;\n}\n\ninterface DerivedClass extends BaseClass {\n    b: string[];\n}\n```\n\nBy default `Serializable` and `Comparable` interfaces are not emitted. You can filter out more interfaces or classes by using the `ignoreSuperclasses` parameter of the `TypeScriptGenerator` constructor.\n\n### Generics\n\n```kotlin\nclass ContrivedExample\u003cA, out B, out C: List\u003cAny\u003e\u003e(\n    private val a: A, \n    val b: B, \n    val c: C,\n    val listOfPairs: List\u003cPair\u003cInt, B\u003e\u003e)\n    \nfun main(args: Array\u003cString\u003e) {\n    println(TypeScriptGenerator(\n        rootClasses = setOf(\n            ContrivedExample::class\n        )\n    ).definitionsText)\n}\n```\n\nThe output is:\n\n```typescript\ninterface Pair\u003cA, B\u003e {\n    first: A;\n    second: B;\n}\n\ninterface ContrivedExample\u003cA, B, C extends any[]\u003e {\n    b: B;\n    c: C;\n    listOfPairs: Pair\u003cnumber, B\u003e[];\n}\n```\n\n### Maps as JS objects\n\n```kotlin\ndata class CardRepository(\n    val cardsByRef: Map\u003cString, Card\u003e)\n```\n\nThe output is:\n\n```typescript\ntype Rarity = \"Normal\" | \"Rare\" | \"SuperRare\";\n\ninterface Card {\n    command: string | null;\n    description: string;\n    generatedTitleLine: string;\n    name: string;\n    rarity: Rarity;\n    ref: string;\n}\n\ninterface CardRepository {\n    cardsByRef: { [key: string]: Card };\n}\n```\n\n### Java beans\n\nSometimes you want to work with long boring Java classes like this one:\n\n```java\npublic class JavaClass {\n    private String name;\n    private int[] results;\n    private boolean finished;\n    private char[][] multidimensional;\n\n    public String getName() { return name; }\n    public void setName(String name) { this.name = name; }\n\n    public int[] getResults() { return results; }\n    public void setResults(int[] results) { this.results = results; }\n\n    // setters are not required for this to work!\n    public boolean isFinished() { return finished; }\n\n    public char[][] getMultidimensional() { return multidimensional; }\n    public void setMultidimensional(char[][] multidimensional) { \n        this.multidimensional = multidimensional; \n    }\n}\n```\n\nEven though its fields are private, they are accessible through getter methods. The generator knows this, so they are included in the definition:\n\n```typescript\ninterface JavaClass {\n    name: string;\n    results: number[];\n    multidimensional: string[][];\n    finished: boolean;\n}\n```\n\n### Java nullability annotations\n\nKotlin was designed with null-safety in mind, but the Java land is not so green.\n\nIn Java all types are nullable by default, so the programmer needs some way to annotate which may and which will never be null. There are many ways to do this, each with its own set of drawbacks.\n\nThe TypeScript generator makes no effort by itself to infer the nullability of Java types. Nevertheless kotlin-reflect is capable of decoding it if the classes are annotated with JSR305 annotations (`javax.annotation.*`). If no annotations are found, the types are assumed to be not null.\n\nNote that `org.jetbrains.annotations.*` and `android.support.annotation.*` **cannot** work for this purpose, as they don't have [runtime retention](https://docs.oracle.com/javase/8/docs/api/java/lang/annotation/Retention.html) and therefore are stripped by the compiler without leaving a way to read them through reflection.\n\nThe following an example of a class with supported annotations:\n\n```java\nimport javax.annotation.Nullable;\nimport javax.annotation.ParametersAreNonnullByDefault;\n\n// Add this to Gradle/Maven to get the annotations:\n// compile 'com.google.code.findbugs:jsr305:3.0.1'\n\n@ParametersAreNonnullByDefault\npublic class JavaClassWithNonnullAsDefault {\n    private int[] results;\n\n    @Nullable\n    private int[] nextResults;\n\n    JavaClassWithNonnullAsDefault(\n        int[] results, \n        @Nullable int[] nextResults)\n    {\n        this.results = results;\n        this.nextResults = nextResults;\n    }\n\n    public int[] getResults() { return results; }\n    public void setResults(int[] results) { this.results = results; }\n\n    @Nullable\n    public int[] getNextResults() { return nextResults; }\n    public void setNextResults(@Nullable int[] nextResults) {\n        this.nextResults = nextResults;\n    }\n}\n```\n\nThe output is the following:\n\n```typescript\ninterface JavaClassWithNonnullAsDefault {\n    name: string;\n    results: number[];\n    nextResults: number[] | null;\n}\n```\n\n### Transformers\n\nSometimes they objects you use in TypeScript or JavaScript are not exactly the same you use in your backend, but have some differences, for instance:\n\n* You may transform one type into another.\n* Your classes may use camelCase in the backend but being turned into snake_case in the frontend by the JSON serializer.\n* Some properties of some classes may be not be sent to the frontend.\n\nTo support cases like these, `TypeScriptGenerator` supports class transformers. They are objects implementing the `ClassTransformer` interface, arranged in a pipeline. They can be used to customize the list of properties of a class and their name and type.\n\nBelow are some examples:\n\n#### Filtering unwanted properties\n\nIn the following example, assume we don't want to emit `ref`:\n\n```kotlin\ndata class Achievement(\n    val ref: String,\n    val title: String,\n    val description: String,\n    val measuredProperty: (player: Player) -\u003e Int,\n    val neededValue: Int\n)\n```\n\nWe can use the `transformPropertyList()` to remove it.\n\n```kotlin\nfun main(args: Array\u003cString\u003e) {\n    println(TypeScriptGenerator(\n        rootClasses = setOf(\n            Achievement::class\n        ),\n        classTransformers = listOf(\n            object : ClassTransformer {\n                override fun transformPropertyList(\n                    properties: List\u003cKProperty\u003c*\u003e\u003e,\n                    klass: KClass\u003c*\u003e\n                ): List\u003cKProperty\u003c*\u003e\u003e {\n                    return properties.filter { property -\u003e\n                        property.name != \"ref\"\n                    }\n                }\n            }\n        )\n    ).definitionsText)\n}\n```\n\nThe output is:\n\n```typescript\ninterface Achievement {\n    description: string;\n    neededValue: number;\n    title: string;\n}\n```\n\n#### Renaming to snake_case\n\nYou can use `transformPropertyName()` to rename any property.\n\nThe functions `camelCaseToSnakeCase()` and `snakeCaseToCamelCase()` are included in this library.\n\n```kotlin\ndata class AchievementCompletionState(\n    val achievementRef: String,\n    val reachedValue: Int)\n\nfun main(args: Array\u003cString\u003e) {\n    println(TypeScriptGenerator(\n        rootClasses = setOf(\n            AchievementCompletionState::class\n        ),\n        classTransformers = listOf(\n            object : ClassTransformer {\n                override fun transformPropertyName(\n                    propertyName: String,\n                    property: KProperty\u003c*\u003e,\n                    klass: KClass\u003c*\u003e\n                ): String {\n                    return camelCaseToSnakeCase(propertyName)\n                }\n            }\n        )\n    ).definitionsText)\n}\n```\n\nThe output is:\n\n```typescript\ninterface AchievementCompletionState {\n    achievement_ref: string;\n    reached_value: number;\n}\n```\n\n#### Replacing types for some properties\n\nImagine in our previous example we don't want to emit `achievement_ref` with type `string`, but rather `achievement`, with type `Achievement`. \n\nWe can use a combination of `transformPropertyName()` and `transformPropertyType()` for this purpose:\n\n```typescript\nfun main(args: Array\u003cString\u003e) {\n    println(TypeScriptGenerator(\n        rootClasses = setOf(\n            AchievementCompletionState::class\n        ),\n        classTransformers = listOf(\n            object : ClassTransformer {\n                override fun transformPropertyName(\n                    propertyName: String, \n                    property: KProperty\u003c*\u003e, \n                    klass: KClass\u003c*\u003e\n                ): String {\n                    if (propertyName == \"achievementRef\") {\n                        return \"achievement\"\n                    } else {\n                        return propertyName\n                    }\n                }\n\n                override fun transformPropertyType(\n                    type: KType, \n                    property: KProperty\u003c*\u003e, \n                    klass: KClass\u003c*\u003e\n                ): KType {\n                    // Note: property is the actual property from the class\n                    // (unless replaced in transformPropertyList()), so\n                    // it maintains the original property name declared\n                    // in the code.\n                    if (property.name == \"achievementRef\") {\n                        return Achievement::class.createType(nullable = false)\n                    } else {\n                        return type\n                    }\n                }\n            }\n        )\n    ).definitionsText)\n}\n```\n\nThe output is:\n\n```typescript\ninterface Achievement {\n    description: string;\n    neededValue: number;\n    ref: string;\n    title: string;\n}\n\ninterface AchievementCompletionState {\n    achievement: Achievement;\n    reachedValue: number;\n}\n```\n\nNote how `Achievement` class is emitted recursively after the transformation has taken place, even though it was not declared in the original `AchievementCompletionState` class nor specified in `rootClasses`.\n\n### Applying transformers only to some classes\n\nTransformers are applied to all classes by default. If you want your transformers to apply only to classes matching a certain predicate, you can wrap them in an instance of `FilteredClassTransformer`. This is its definition:\n \n ```kotlin\nclass FilteredClassTransformer(\n    val wrappedTransformer: ClassTransformer,\n    val filter: (klass: KClass\u003c*\u003e) -\u003e Boolean\n): ClassTransformer\n```\n\nFor the common case of applying a transformer only on a class and its subclasses if any, an extension method is provided, `.onlyOnSubclassesOf()`:\n \n```kotlin\nfun main(args: Array\u003cString\u003e) {\n    println(TypeScriptGenerator(\n        rootClasses = setOf(\n            Achievement::class\n        ),\n        classTransformers = listOf(\n            object : ClassTransformer {\n                override fun transformPropertyList(\n                    properties: List\u003cKProperty\u003c*\u003e\u003e,\n                    klass: KClass\u003c*\u003e\n                ): List\u003cKProperty\u003c*\u003e\u003e {\n                    return properties.filter { property -\u003e\n                        property.name != \"ref\"\n                    }\n                }\n            }.onlyOnSubclassesOf(Achievement::class)\n        )\n    ).definitionsText)\n}\n```\n\n### Optional\\\u003cT\\\u003e unwrapping\n\nThis is an example of a more complex transformer that can be used to unwrap `Optional\u003cT\u003e` into `T | null`.\n\nLet's suppose a Java class like this:\n\n```java\npublic class JavaClassWithOptional {\n    private String name;\n    private String surname;\n\n    public Optional\u003cString\u003e getSurname() {\n        return Optional.ofNullable(surname);\n    }\n\n    public String getName() {\n        return name;\n    }\n}\n```\n\nWe could use this transformer:\n\n```kotlin\nobject : ClassTransformer {\n    override fun transformPropertyType(\n        type: KType,\n        property: KProperty\u003c*\u003e,\n        klass: KClass\u003c*\u003e\n    ): KType {\n        val bean = Introspector.getBeanInfo(klass.java)\n            .propertyDescriptors\n            .find { it.name == property.name }\n\n        val getterReturnType = bean?.readMethod?.kotlinFunction?.returnType\n        if (getterReturnType?.classifier == Optional::class) {\n            val wrappedType = getterReturnType.arguments.first().type!!\n            return wrappedType.withNullability(true)\n        } else {\n            return type\n        }\n    }\n}\n```\n\nThe result would be this:\n\n```typescript\ninterface JavaClassWithOptional {\n    name: string;\n    surname: string | null;\n}\n```\n\n# License\n\n```text\nCopyright 2017 Alicia Boya García\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n   http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fntrrgc%2Fts-generator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fntrrgc%2Fts-generator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fntrrgc%2Fts-generator/lists"}