Closed Polymorphism
https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/polymorphism.md#closed-polymorphism
Static types
@Serializable
open class Project(val name: String)
class OwnedProject(name: String, val owner: String) : Project(name)
fun main() {
val tsGenerator = KxsTsGenerator()
println(tsGenerator.generate(Project.serializer()))
}
You can get the full code here.
Since OwnedProject
is not @Serializable
, only the properties of Project
generated.
export interface Project {
name: string;
}
import kotlinx.serialization.modules.*
@Serializable
abstract class Project {
abstract val name: String
}
@Serializable
class OwnedProject(override val name: String, val owner: String) : Project()
val module = SerializersModule {
polymorphic(Project::class) {
subclass(OwnedProject::class)
}
}
fun main() {
val config = KxsTsConfig(serializersModule = module)
val tsGenerator = KxsTsGenerator(config)
println(tsGenerator.generate(Project.serializer()))
}
You can get the full code here.
export type Project = any;
// export interface Project {
// name: string;
// }
//
// export interface OwnedProject extends Project {
// name: string;
// owner: string;
// }
Sealed classes
Sealed classes are the best way to generate TypeScript interface so far, because all subclasses are
defined in the SerialDescriptor
.
A sealed class will be converted as a union enum, with enum member types .
This has many benefits that closely match how sealed classes work in Kotlin.
@Serializable
sealed class Project {
abstract val name: String
}
@Serializable
@SerialName("OProj")
class OwnedProject(override val name: String, val owner: String) : Project()
@Serializable
class DeprecatedProject(override val name: String, val reason: String) : Project()
fun main() {
val tsGenerator = KxsTsGenerator()
println(tsGenerator.generate(Project.serializer()))
}
You can get the full code here.
export type Project =
| Project.DeprecatedProject
| Project.OProj;
export namespace Project {
export enum Type {
DeprecatedProject = "dev.adamko.kxstsgen.example.examplePolymorphicSealedClass01.DeprecatedProject",
OProj = "OProj",
}
export interface DeprecatedProject {
type: Project.Type.DeprecatedProject;
name: string;
reason: string;
}
export interface OProj {
type: Project.Type.OProj;
name: string;
owner: string;
}
}
Nested sealed classes
Nested sealed classes are 'invisible' to Kotlinx Serialization. In this
example, sealed class Retriever
is ignored.
For now, it's recommended to avoid nested sealed classes.
@Serializable
sealed class Dog {
abstract val name: String
@Serializable
@SerialName("Dog.Mutt")
class Mutt(override val name: String, val loveable: Boolean = true) : Dog()
@Serializable
sealed class Retriever : Dog() {
abstract val colour: String
@Serializable
@SerialName("Dog.Retriever.Golden")
data class Golden(
override val name: String,
override val colour: String,
val cute: Boolean = true,
) : Retriever()
@Serializable
@SerialName("Dog.Retriever.NovaScotia")
data class NovaScotia(
override val name: String,
override val colour: String,
val adorable: Boolean = true,
) : Retriever()
}
}
fun main() {
val tsGenerator = KxsTsGenerator()
println(tsGenerator.generate(Dog.serializer()))
}
You can get the full code here.
export type Dog =
| Dog.Golden
| Dog.Mutt
| Dog.NovaScotia;
export namespace Dog {
export enum Type {
Mutt = "Dog.Mutt",
Golden = "Dog.Retriever.Golden",
NovaScotia = "Dog.Retriever.NovaScotia",
}
export interface Mutt {
type: Dog.Type.Mutt;
name: string;
loveable?: boolean;
}
export interface Golden {
type: Dog.Type.Golden;
name: string;
colour: string;
cute?: boolean;
}
export interface NovaScotia {
type: Dog.Type.NovaScotia;
name: string;
colour: string;
adorable?: boolean;
}
}
// Nested sealed classes don't work at the moment :(
// export type Dog = Dog.Mutt | Dog.Retriever
//
// export namespace Dog {
// export enum Type {
// Mutt = "Mutt",
// }
//
// export interface Mutt {
// type: Type.Mutt;
// name: string;
// loveable?: boolean;
// }
//
// export type Retriever = Retriever.Golden | Retriever.NovaScotia
//
// export namespace Retriever {
// export enum Type {
// Golden = "Golden",
// NovaScotia = "NovaScotia",
// }
//
// export interface Golden {
// type: Type.Golden;
// name: string;
// cute?: boolean;
// }
//
// export interface NovaScotia {
// type: Type.NovaScotia;
// name: string;
// adorable?: boolean;
// }
// }
// }
Objects
@Serializable
sealed class Response
@Serializable
object EmptyResponse : Response()
@Serializable
class TextResponse(val text: String) : Response()
fun main() {
val tsGenerator = KxsTsGenerator()
println(
tsGenerator.generate(Response.serializer())
)
}
You can get the full code here.
export type Response =
| Response.EmptyResponse
| Response.TextResponse;
export namespace Response {
export enum Type {
EmptyResponse = "dev.adamko.kxstsgen.example.examplePolymorphicObjects01.EmptyResponse",
TextResponse = "dev.adamko.kxstsgen.example.examplePolymorphicObjects01.TextResponse",
}
export interface EmptyResponse {
type: Response.Type.EmptyResponse;
}
export interface TextResponse {
type: Response.Type.TextResponse;
text: string;
}
}