Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Kotlin feature like Typescript Partial?

tl;dr; What's the easiest way to convert

data class User(val organization: Organization)

to

data class UserNullable(val organization: Organization?)

so I have both available as options?

Long story:

I'm writing Spring Boot integration tests for my Kotlin REST app and running into difficulties deserializing from JSON. For example, I have a User class and it has a non-nullable Organization inside it. But sometimes I want to return a user without their organization, so I just don't load the organization in Hibernate and it automatically gets serialized to null by Jackson.

In Typescript there's a feature called Partial that lets you create a version of a class where all its fields are considered nullable. What would be the easiest way to do that with Kotlin? I don't want to destroy the null-safety of all my entity classes. I also don't want to create an entire superfluous DTO layer.

I know I could make my classes all use late init var instead of the constructor parameter vals, but that would remove the immutability of the classes, which I also think is valuable.

like image 944
CorayThan Avatar asked Jun 01 '18 22:06

CorayThan


1 Answers

One possible solution for the minimal example you have given would be to introduce a bounded type parameter for the value, like so:

class UserGeneric<T : Organization?>(val organization: T)

typealias User = UserGeneric<Organization>
typealias UserNullable = UserGeneric<Organization?>

But that would get very tedious and unreadable once you have (data) classes with lots of fields, because every single one would need an associated type parameter. So the short answer is: No, there's no such thing as TypeScript's Partial in Kotlin.

You will have to resort to actually write these classes twice. But if you do that, one thing that can help you is the fact that nullable types in Kotlin are always considered to be the supertypes of their non-nullable counterparts. Which means, you can develop a helpful inheritance structure between your nullable and non-nullable User classes:

open class UserNullable {
    open val organization: Organization? // initialize or write constructor(s)
}

class User : UserNullable() {
    override val organization: Organization // initialize or write constructor(s)
}

which means that now all of your User instances are assignable to the UserNullable type. For example, you can write a function that takes UserNullable as a parameter, but you can also pass a User.

You might end up designing the inheritance hierarchy a bit nicer, for example by specifying your fields in the primary constructor or somehow using interfaces instead. The above snippet only serves as a demonstration for how that inheritance structure could work.

Finally, your last option is to just use the nullable version everywhere. It's definitely a bit more tedious but Kotlin's nullability handling tools (safe calls, elvis, non-null assert, smart casts, ...) are quite powerful and it might not be so bad after all.

like image 184
Raphael Tarita Avatar answered Jun 29 '23 01:06

Raphael Tarita