Giter VIP home page Giter VIP logo

ts-generator's Introduction

TypeScript definition generator for the JVM

This library generates TypeScript definitions that cover a set of Kotlin and Java classes using Kotlin reflection.

TypeScript definitions are useful when data classes are serialized to JSON and handled in a JavaScript or TypeScript web frontend as they enable context-aware type checking and autocompletion in a number of IDEs and editors.

ts-generator supports:

  • Primitive types, with or without explicit int.
  • Kotlin and Java classes.
  • Data classes.
  • Enums.
  • Any type.
  • Generic classes, without type erasure.
  • Generic constraints.
  • Class inheritance.
  • Abstract classes.
  • Lists as JS arrays.
  • Maps as JS objects.
  • Null safety, even inside composite types.
  • Java beans.
  • Mapping types.
  • Nullability annotations, when allowed by the retention policy.
  • Customizing class definitions via transformers.
  • Parenthesis optimization: They are placed only when they are needed to disambiguate.
  • Emitting either null or undefined for JVM nullable types.

Installation

ts-generator requires Kotlin 1.1. Kotlin 1.0 is not compatible as its reflection library is not powerful enough to do this transformation.

Then you need to include this library in your project. The easiest way is to download it from JitPack. For instance, in Gradle you would add this to build.gradle:

repositories {
    maven { url 'https://jitpack.io' }
}

dependencies {
    compile 'com.github.ntrrgc:ts-generator:1.1.1'
}

Basic usage

The first you need is your Kotlin or Java classes or interfaces, for instance:

enum class Rarity(val abbreviation: String) {
    Normal("N"),
    Rare("R"),
    SuperRare("SR"),
}

data class Card(
    val ref: String,
    val rarity: Rarity,
    val name: String,
    val description: String,
    val command: String?,
    val playCard: (() -> Unit)?
) {
    val generatedTitleLine = "*$name* [$rarity]"
}

data class Inventory(
    val cards: List<Card> = listOf()
)

data class Player(
    val name: String,
    val inventory: Inventory = Inventory(),
    val achievementsProgress: List<AchievementCompletionState> = listOf(),
    val notices: List<Notice> = listOf()
)

data class Notice(
    val dateTime: LocalDateTime,
    val text: String
)

data class Achievement(
    val ref: String,
    val title: String,
    val description: String,
    val measuredProperty: (player: Player) -> Int,
    val neededValue: Int
)

data class AchievementCompletionState(
    val achievementRef: String,
    val reachedValue: Int
)

Then use TypeScriptGenerator to generate the TypeScript definitions, like this:

fun main(args: Array<String>) {
    println(TypeScriptGenerator(
        rootClasses = setOf(
            Player::class
        ),
        mappings = mapOf(
            LocalDateTime::class to "Date",
            LocalDate::class to "Date"
        )
    ).definitionsText)
}

You will get an output like this:

interface AchievementCompletionState {
    achievementRef: string;
    reachedValue: number;
}

type Rarity = "Normal" | "Rare" | "SuperRare";

interface Card {
    command: string | null;
    description: string;
    generatedTitleLine: string;
    name: string;
    rarity: Rarity;
    ref: string;
}

interface Inventory {
    cards: Card[];
}

interface Notice {
    dateTime: Date;
    text: string;
}

interface Player {
    achievementsProgress: AchievementCompletionState[];
    inventory: Inventory;
    name: string;
    notices: Notice[];
}

Then you can paste it into a .d.ts file and declare it in your environment, e.g:

Advanced features

This generator can handle more complex data types. Some examples are shown below:

Mapping types

Sometimes you want to map certain Kotlin or Java classes to native JS types, like Date.

This can be done with the mappings argument of TypeScriptGenerator, as show in the first example.

Note 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.

Int type

Currently TypeScript only supports one number type: number.

This may change if a proposal for int types succeeds. Also, some people may want to be extra explicit and do:

type int = number;

In 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:

fun main(args: Array<String>) {
    println(TypeScriptGenerator(
        rootClasses = setOf(
            AchievementCompletionState::class
        ),
        intTypeName = "int"
    ).definitionsText)
}

The output will be:

interface AchievementCompletionState {
    achievementRef: string;
    reachedValue: int;
}

Inheritance support

open class BaseClass(val a: Int)

class DerivedClass(val b: List<String>): BaseClass(4)

fun main(args: Array<String>) {
    println(TypeScriptGenerator(
        rootClasses = setOf(
            DerivedClass::class
        )
    ).definitionsText)
}

The output is:

interface BaseClass {
    a: number;
}

interface DerivedClass extends BaseClass {
    b: string[];
}

By 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.

Generics

class ContrivedExample<A, out B, out C: List<Any>>(
    private val a: A, 
    val b: B, 
    val c: C,
    val listOfPairs: List<Pair<Int, B>>)
    
fun main(args: Array<String>) {
    println(TypeScriptGenerator(
        rootClasses = setOf(
            ContrivedExample::class
        )
    ).definitionsText)
}

The output is:

interface Pair<A, B> {
    first: A;
    second: B;
}

interface ContrivedExample<A, B, C extends any[]> {
    b: B;
    c: C;
    listOfPairs: Pair<number, B>[];
}

Maps as JS objects

data class CardRepository(
    val cardsByRef: Map<String, Card>)

The output is:

type Rarity = "Normal" | "Rare" | "SuperRare";

interface Card {
    command: string | null;
    description: string;
    generatedTitleLine: string;
    name: string;
    rarity: Rarity;
    ref: string;
}

interface CardRepository {
    cardsByRef: { [key: string]: Card };
}

Java beans

Sometimes you want to work with long boring Java classes like this one:

public class JavaClass {
    private String name;
    private int[] results;
    private boolean finished;
    private char[][] multidimensional;

    public String getName() { return name; }
    public void setName(String name) { this.name = name; }

    public int[] getResults() { return results; }
    public void setResults(int[] results) { this.results = results; }

    // setters are not required for this to work!
    public boolean isFinished() { return finished; }

    public char[][] getMultidimensional() { return multidimensional; }
    public void setMultidimensional(char[][] multidimensional) { 
        this.multidimensional = multidimensional; 
    }
}

Even though its fields are private, they are accessible through getter methods. The generator knows this, so they are included in the definition:

interface JavaClass {
    name: string;
    results: number[];
    multidimensional: string[][];
    finished: boolean;
}

Java nullability annotations

Kotlin was designed with null-safety in mind, but the Java land is not so green.

In 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.

The 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.

Note that org.jetbrains.annotations.* and android.support.annotation.* cannot work for this purpose, as they don't have runtime retention and therefore are stripped by the compiler without leaving a way to read them through reflection.

The following an example of a class with supported annotations:

import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;

// Add this to Gradle/Maven to get the annotations:
// compile 'com.google.code.findbugs:jsr305:3.0.1'

@ParametersAreNonnullByDefault
public class JavaClassWithNonnullAsDefault {
    private int[] results;

    @Nullable
    private int[] nextResults;

    JavaClassWithNonnullAsDefault(
        int[] results, 
        @Nullable int[] nextResults)
    {
        this.results = results;
        this.nextResults = nextResults;
    }

    public int[] getResults() { return results; }
    public void setResults(int[] results) { this.results = results; }

    @Nullable
    public int[] getNextResults() { return nextResults; }
    public void setNextResults(@Nullable int[] nextResults) {
        this.nextResults = nextResults;
    }
}

The output is the following:

interface JavaClassWithNonnullAsDefault {
    name: string;
    results: number[];
    nextResults: number[] | null;
}

Transformers

Sometimes they objects you use in TypeScript or JavaScript are not exactly the same you use in your backend, but have some differences, for instance:

  • You may transform one type into another.
  • Your classes may use camelCase in the backend but being turned into snake_case in the frontend by the JSON serializer.
  • Some properties of some classes may be not be sent to the frontend.

To 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.

Below are some examples:

Filtering unwanted properties

In the following example, assume we don't want to emit ref:

data class Achievement(
    val ref: String,
    val title: String,
    val description: String,
    val measuredProperty: (player: Player) -> Int,
    val neededValue: Int
)

We can use the transformPropertyList() to remove it.

fun main(args: Array<String>) {
    println(TypeScriptGenerator(
        rootClasses = setOf(
            Achievement::class
        ),
        classTransformers = listOf(
            object : ClassTransformer {
                override fun transformPropertyList(
                    properties: List<KProperty<*>>,
                    klass: KClass<*>
                ): List<KProperty<*>> {
                    return properties.filter { property ->
                        property.name != "ref"
                    }
                }
            }
        )
    ).definitionsText)
}

The output is:

interface Achievement {
    description: string;
    neededValue: number;
    title: string;
}

Renaming to snake_case

You can use transformPropertyName() to rename any property.

The functions camelCaseToSnakeCase() and snakeCaseToCamelCase() are included in this library.

data class AchievementCompletionState(
    val achievementRef: String,
    val reachedValue: Int)

fun main(args: Array<String>) {
    println(TypeScriptGenerator(
        rootClasses = setOf(
            AchievementCompletionState::class
        ),
        classTransformers = listOf(
            object : ClassTransformer {
                override fun transformPropertyName(
                    propertyName: String,
                    property: KProperty<*>,
                    klass: KClass<*>
                ): String {
                    return camelCaseToSnakeCase(propertyName)
                }
            }
        )
    ).definitionsText)
}

The output is:

interface AchievementCompletionState {
    achievement_ref: string;
    reached_value: number;
}

Replacing types for some properties

Imagine in our previous example we don't want to emit achievement_ref with type string, but rather achievement, with type Achievement.

We can use a combination of transformPropertyName() and transformPropertyType() for this purpose:

fun main(args: Array<String>) {
    println(TypeScriptGenerator(
        rootClasses = setOf(
            AchievementCompletionState::class
        ),
        classTransformers = listOf(
            object : ClassTransformer {
                override fun transformPropertyName(
                    propertyName: String, 
                    property: KProperty<*>, 
                    klass: KClass<*>
                ): String {
                    if (propertyName == "achievementRef") {
                        return "achievement"
                    } else {
                        return propertyName
                    }
                }

                override fun transformPropertyType(
                    type: KType, 
                    property: KProperty<*>, 
                    klass: KClass<*>
                ): KType {
                    // Note: property is the actual property from the class
                    // (unless replaced in transformPropertyList()), so
                    // it maintains the original property name declared
                    // in the code.
                    if (property.name == "achievementRef") {
                        return Achievement::class.createType(nullable = false)
                    } else {
                        return type
                    }
                }
            }
        )
    ).definitionsText)
}

The output is:

interface Achievement {
    description: string;
    neededValue: number;
    ref: string;
    title: string;
}

interface AchievementCompletionState {
    achievement: Achievement;
    reachedValue: number;
}

Note 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.

Applying transformers only to some classes

Transformers 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:

class FilteredClassTransformer(
   val wrappedTransformer: ClassTransformer,
   val filter: (klass: KClass<*>) -> Boolean
): ClassTransformer

For the common case of applying a transformer only on a class and its subclasses if any, an extension method is provided, .onlyOnSubclassesOf():

fun main(args: Array<String>) {
    println(TypeScriptGenerator(
        rootClasses = setOf(
            Achievement::class
        ),
        classTransformers = listOf(
            object : ClassTransformer {
                override fun transformPropertyList(
                    properties: List<KProperty<*>>,
                    klass: KClass<*>
                ): List<KProperty<*>> {
                    return properties.filter { property ->
                        property.name != "ref"
                    }
                }
            }.onlyOnSubclassesOf(Achievement::class)
        )
    ).definitionsText)
}

Optional<T> unwrapping

This is an example of a more complex transformer that can be used to unwrap Optional<T> into T | null.

Let's suppose a Java class like this:

public class JavaClassWithOptional {
    private String name;
    private String surname;

    public Optional<String> getSurname() {
        return Optional.ofNullable(surname);
    }

    public String getName() {
        return name;
    }
}

We could use this transformer:

object : ClassTransformer {
    override fun transformPropertyType(
        type: KType,
        property: KProperty<*>,
        klass: KClass<*>
    ): KType {
        val bean = Introspector.getBeanInfo(klass.java)
            .propertyDescriptors
            .find { it.name == property.name }

        val getterReturnType = bean?.readMethod?.kotlinFunction?.returnType
        if (getterReturnType?.classifier == Optional::class) {
            val wrappedType = getterReturnType.arguments.first().type!!
            return wrappedType.withNullability(true)
        } else {
            return type
        }
    }
}

The result would be this:

interface JavaClassWithOptional {
    name: string;
    surname: string | null;
}

License

Copyright 2017 Alicia Boya Garcรญa

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

ts-generator's People

Contributors

fsmorygo avatar hpoul avatar jensim avatar ntrrgc avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

ts-generator's Issues

Kotlin Map with enum key should use [item in Enum] instead of [item: Enum]

enum class Key {
  first,
  second
}

class Container {
  values: Map<Key, string>
}

now transform into

type Key = 'first' | 'second'
interface Container {
  values: [key: Key]: string;
}

This is an error from perspective of TypeScript
it should be that instead:

type Key = 'first' | 'second'
interface Container {
  values: [key in Key]: string;
}

Output properties in declaration order

It seems that properties are output in alphabetical order. It seems to me that they should be generated in declaration order (in the kotlin definition). At the very least that should be configurable.

exception when running from java

pom.xml

...
       <dependency>
            <groupId>com.github.ntrrgc</groupId>
            <artifactId>ts-generator</artifactId>
            <version>1.1.1</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-stdlib-jdk8</artifactId>
            <version>1.3.20</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.jetbrains.kotlin</groupId>
            <artifactId>kotlin-reflect</artifactId>
            <version>1.3.20</version>
            <scope>test</scope>
        </dependency>

sample test

package com.vg.js.player;

import kotlin.reflect.KClass;
import me.ntrrgc.tsGenerator.TypeScriptGenerator;
import me.ntrrgc.tsGenerator.VoidType;
import org.junit.Test;

import java.util.Collections;

import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap;
import static java.util.Collections.emptySet;

public class TsGeneratorTest {
    @Test
    public void testGenerate() {
        Iterable<? extends KClass<?>> rootClasses = Collections.singleton(kotlin.jvm.JvmClassMappingKt.getKotlinClass(PlayerImpl.class));
        TypeScriptGenerator tsg = new TypeScriptGenerator(rootClasses, emptyMap(), emptyList(), emptySet(), "number", VoidType.NULL);
        System.out.println(tsg.getDefinitionsText());
    }
}

Exception produced:

java.lang.ExceptionInInitializerError
	at com.vg.js.player.TsGeneratorTest.testGenerate(TsGeneratorTest.java:14)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
	at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
	at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
	at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
	at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
	at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: java.lang.IllegalStateException: Resource not found in classpath: kotlin/coroutines/coroutines.kotlin_builtins
	at kotlin.reflect.jvm.internal.impl.serialization.deserialization.builtins.BuiltInsLoaderImpl.createBuiltInPackageFragmentProvider(BuiltInsLoaderImpl.kt:55)
	at kotlin.reflect.jvm.internal.impl.serialization.deserialization.builtins.BuiltInsLoaderImpl.createPackageFragmentProvider(BuiltInsLoaderImpl.kt:33)
	at kotlin.reflect.jvm.internal.impl.builtins.KotlinBuiltIns.createBuiltInsModule(KotlinBuiltIns.java:127)
	at kotlin.reflect.jvm.internal.impl.builtins.jvm.JvmBuiltIns.<init>(JvmBuiltIns.kt:43)
	at kotlin.reflect.jvm.internal.impl.builtins.jvm.JvmBuiltIns.<init>(JvmBuiltIns.kt:18)
	at kotlin.reflect.jvm.internal.components.RuntimeModuleData$Companion.create(RuntimeModuleData.kt:58)
	at kotlin.reflect.jvm.internal.ModuleByClassLoaderKt.getOrCreateModule(moduleByClassLoader.kt:58)
	at kotlin.reflect.jvm.internal.KDeclarationContainerImpl$Data$moduleData$2.invoke(KDeclarationContainerImpl.kt:37)
	at kotlin.reflect.jvm.internal.KDeclarationContainerImpl$Data$moduleData$2.invoke(KDeclarationContainerImpl.kt:34)
	at kotlin.reflect.jvm.internal.ReflectProperties$LazySoftVal.invoke(ReflectProperties.java:92)
	at kotlin.reflect.jvm.internal.ReflectProperties$Val.getValue(ReflectProperties.java:31)
	at kotlin.reflect.jvm.internal.KDeclarationContainerImpl$Data.getModuleData(KDeclarationContainerImpl.kt)
	at kotlin.reflect.jvm.internal.KClassImpl$Data$descriptor$2.invoke(KClassImpl.kt:47)
	at kotlin.reflect.jvm.internal.KClassImpl$Data$descriptor$2.invoke(KClassImpl.kt:44)
	at kotlin.reflect.jvm.internal.ReflectProperties$LazySoftVal.invoke(ReflectProperties.java:92)
	at kotlin.reflect.jvm.internal.ReflectProperties$Val.getValue(ReflectProperties.java:31)
	at kotlin.reflect.jvm.internal.KClassImpl$Data.getDescriptor(KClassImpl.kt)
	at kotlin.reflect.jvm.internal.KClassImpl.getDescriptor(KClassImpl.kt:179)
	at kotlin.reflect.jvm.internal.KClassImpl.getDescriptor(KClassImpl.kt:43)
	at kotlin.reflect.full.KClassifiers.createType(KClassifiers.kt:44)
	at kotlin.reflect.full.KClassifiers.createType$default(KClassifiers.kt:42)
	at me.ntrrgc.tsGenerator.TypeScriptGenerator.<clinit>(TypeScriptGenerator.kt:99)
	... 23 more

tag version 1.1.2

Hi again ๐Ÿ‘‹๐Ÿ˜
Ive you like the version bumps from #27, maybe we could have a tag?
Best regards
Jens

String Enums

Wondering if it's possible to generate string-enums instead of type aliases?

Compile from Kotlin to JS with type definitions

First let me say this isn't a problem I'm expecting the maintainer of this repository to fix. I just thought they might be in a good position to comment on what's possible.

I have a Kotlin library I want to use on the JVM and in TypeScript. Using the official Kotlin2JS compiler, I can compile my code to JS. But then I don't get type definitions. And using this library I can generate type definitions, but not the actual implementation.

Is there a good way of combining the two? Do you know if JetBrains plan to provide type definition generation as part of their Kotlin2JS compiler?

Propose an option to generate Optionals Instead of Nullables

Hello and thanks for this project.

I'd like to propose that nullable kotlin types generate T | undefined instead of T | null.

As you certainly know, javascript has two bottom types: null and undefined.
In Typescript they both have a slightly different meaning.

A type T | null is sometimes called Nullable (type Nullable<T> = T | null).
When you turn on strict null checks, typescript will force you to deal with the null value before deferencing them.

A type T | undefined is sometimes called Optional (type Optional<T> = T | undefined).
It also has a special syntax with a question mark (field?: T).

The main difference between those types is highlighted in the following snippet:

class NullablePerson {
  lastName: string | null
}
class OptionalPerson {
  lastName?: string
}

const p1: NullablePerson = {}; // does not compile
const p2: NullablePerson = {lastName: null}; // compiles

const p3: OptionalPerson = {}; // compiles
const p4: OptionalPerson = {lastName: null}; // does not compile
const p5: OptionalPerson = {lastName: undefined}; // compiles

When I consume a REST API, there are two cases:

  1. I receive data from the server (GET)

In that case, both Optional and nullable will force me to check before deferrencing nullable values.
There is no difference between the two approaches.

  1. I send data to the server (POST, PUT, PATCH)

In this case, I probably should not bother sending null fields.
I would rather have Optionals

Jackson

Jackson has a feature that prevents null from being serialized.

Ts-lint

Ts-lint has a rule that enforces the use of undefined instead of null.

Also relevant:

Article - Null is bad

What do you think? I could open a PR if you wish.

Field ordering

I noticed #13 was closed, but I'm also interested in being able to preserve sort order. I've integrated the library and have really enjoyed it, but being able to preserve field order is important enough that I fear it will ultimately result in us not being able to use the library without this feature as we are currently still manually sorting fields by hand post generation.

I see that the field ordering is based on Kotlin's reflection library, but am curious if you have any ideas on how to implement this feature?

I also noticed the ClassTransfomer.transformPropertyList(properties: List<KProperty<*>>, klass: KClass<*>): List<KProperty<*>> which looked promising but I haven't dug in very deeply.

Thanks!

Sharing constants

Hello. Is there any way to share constants between kotlin and typescript?

For example:

object ApiConstants {
    const val SOME_VAL_1 = 1234
    const val SOME_VAL_2 = "hello world"
}

Expected TS output:

let ApiConstants = {
    SOME_VAL_1: 1234,
    SOME_VAL_2: "hello world"
}

Nullable type from mappings

Hi!

First of all, thanks for your great work :)

I have created a type mapping from UUID to 'string', which does seem to work. My problem is that if the UUID is nullable in kotlin, the typescript type is not generated as such.

Is this supposed to be liket that?

Feature Request: ability to annotate integer fields

Goal: convey the concept of an integer to typescript, given that typescript doesn't have this concept, using JSON Schema.

Given Kotlin classes, we generate 2 files:

  1. Typescript using ts-generator.
  2. JSON Schema from that typescript using typescript-json-schema.

Then at runtime, we can validate any JSON object against the schema using ajv. This works fine.

Now the question of integers.

Given:

export interface Shape {
    /**
     * @minimum 0
     * @TJS-type integer
     */
    size: number;
}

Then:

  • typescript-json-schema can create the correct JSON Schema, and
  • ajv can validate it.

So, the questions are these:

  • is there a way for ts-generator to add // @TJS-type integer to the generated typescript number?
  • is there a way to decorate an integer such that it also outputs // @minimum 0?

If not, then one way around this I guess would be to use a special kotlin class for these integer types, which is manually edited one-time in the schema, but that seems a bit clunky, especially in a CI context.

Enums as enums

Wouldn't it make more sense to map enums to enums instead of union types? It would be even better if a flag could provided, whether one wants number based enums, string based enums, or, if you want to, union types.

Support for functions

Right now, as far as I can tell, this library only allows generating data classes and the like.

It would be very useful if it could also generate interfaces with methods.
Is that something that you would like ts-generator to support?

Support for sealed classes

Would be nice to see sealed class to union type support, e.g.

sealed class Thing {
  data class ThingOne(val value: String): Thing()
  data class ThingTwo(val otherValue: Int): Thing()
}

Would compile to:

interface ThingOne {
  value: string;
} 
interface ThingTwo {
  otherValue: number;
}
type Thing = ThingOne | ThingTwo;

How do we prevent computed properties from being generated

I have a data class with a computed property. The computed property is also present in the generated interface.

data class Test(
   val isOn: Boolean
) {
  val isOff: Boolean
    get() {
        return !isOn
    }
}

Generated Interface

interface Test {
  isOn: boolean
  isOff: boolean
}

Is there a way to turn off generation of computed properties?

Gradle plugin

This is a very cool project. However, wouldn't it make more sense to run this as part of a Gradle plugin, during the build process, and not as a part of the application's code itself?

I see this as something similar to the kotlin2js plugin.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.