Typealias permits breaking bounds of generic argument

I wanted to get some clarity around how typealiases work with generic boundaries.

data class Entity<I : Any, T : Any>(
    val id: I,
    val type: T
)

typealias RetrieveEntity<I, T> = (I) -> Entity<I, T>

fun interface EntityRepository<I, T> : RetrieveEntity<I, T>

I have an Entity class made of two parameters, neither can be null. I’ve defined a RetrieveEntity alias as a function that takes in an id, and returns an Entity with that id. I’ve also defined an EntityRepository functional interface that uses the RetrieveEntity alias.

I know that I can’t specify any type boundaries on the typealias. This lead me to believe that the type boundaries would be inferred/implied based on the aliased type, in this case Entity.

It turns out though, that I can define an EntityRepository alias with nullable types.

typealias StringIntRepository = EntityRepository<String, Int>
typealias NullableStringIntRepository = EntityRepository<String?, Int?>

This doesn’t cause any compilation issues, until I try to make an implementation of the EntityRepository.

class StringIntRepositoryImpl : StringIntRepository {
    override fun invoke(p1: String): Entity<String, Int> {
        TODO("compiles fine")
    }
}

class NullableStringIntRepositoryImpl : NullableStringIntRepository {
    override fun invoke(p1: String?): Entity<String?, Int?> {
        TODO("does not compile: Type argument is not within its bounds")
    }
}

val y = EntityRepository<String?, Int?> {
    TODO("compiles fine")
}

It seems the issue gets pushed down to the lowest possible point before a compilation error occurs. No compilation error happens until we try to reference an Entity with nullable boundaries; preventing construction, or preventing explicitly mentioning the return type. We can define type aliases that can’t effectively use, due to the boundaries on the aliased type.

But, even though I can define an EntityRepository this way, I can’t actually define an alias for the RetrieveEntity this way.

typealias NullableStringIntRetrieveEntity = RetrieveEntity<String?, Int?>
// Type argument resulting from type alias expansion is not within required bounds for 'I': should be subtype of 'Any', substituted type is 'String?'

It seems like this error should also occur on the interface definition, but it doesn’t.

Is this the expected behavior?

1 Like

reported: https://youtrack.jetbrains.com/issue/KT-48190

3 Likes