开源软件名称(OpenSource Name):ntrrgc/ts-generator开源软件地址(OpenSource Url):https://github.com/ntrrgc/ts-generator开源编程语言(OpenSource Language):Kotlin 88.3%开源软件介绍(OpenSource Introduction):TypeScript definition generator for the JVMThis 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:
Installationts-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 repositories {
maven { url 'https://jitpack.io' }
}
dependencies {
compile 'com.github.ntrrgc:ts-generator:1.1.1'
} Basic usageThe 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 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
Advanced featuresThis generator can handle more complex data types. Some examples are shown below: Mapping typesSometimes you want to map certain Kotlin or Java classes to native JS types, like This can be done with the 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 typeCurrently TypeScript only supports one number type: 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 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 supportopen 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 Genericsclass 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 objectsdata 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 beansSometimes 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 annotationsKotlin 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 ( Note that 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;
} TransformersSometimes they objects you use in TypeScript or JavaScript are not exactly the same you use in your backend, but have some differences, for instance:
To support cases like these, Below are some examples: Filtering unwanted propertiesIn the following example, assume we don't want to emit data class Achievement(
val ref: String,
val title: String,
val description: String,
val measuredProperty: (player: Player) -> Int,
val neededValue: Int
) We can use the 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_caseYou can use The functions 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 propertiesImagine in our previous example we don't want to emit We can use a combination of 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 Applying transformers only to some classesTransformers 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 class FilteredClassTransformer(
val wrappedTransformer: ClassTransformer,
val filter: (klass: KClass<*>) -> Boolean
): ClassTransformer For the common case of applying a transformer only on a class and i |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论