1

How can I get the concrete type of a super class type parameters from a subclass?

Assuming that I have a generic super class, an intermediate class and a subclass as follow:

class SuperClass[A, B]
class IntClass[A] extends SuperClass[A, Int]
class MyClass extends SuperClass[String, Int]

I'd like a function "getTypeArgs" that returns type parameters of SuperClass:

val superClassType = typeOf[SuperClass[_, _]]

val superClassOfStringInt = appliedType(superClassType.typeConstructor, typeOf[String], typeOf[Int])
getTypeArgs(superClassOfStringInt, superClassType)
// [TEST1] should return List(String, Int)

getTypeArgs(typeOf[MyClass], superClassType)
// [TEST2] should return List(String, Int)

val intClassOfLong = appliedType(typeOf[IntClass[_]].typeConstructor, typeOf[Long])
getTypeArgs(intClassOfLong, superClassType)
// [TEST3] should return List(Long, Int)

I've tried the solution In Scala Reflection, How to get generic type parameter of a concrete subclass?:

def getTypeArgs(t: Type, fromType: Type): List[Type] = {
  internal
    .thisType(t.dealias.typeSymbol)
    .baseType(fromType.typeSymbol.asClass)
    .typeArgs
}

It works for TEST2, but TEST1 returns List(A, B) and TEST3 returns List(A, Int).

I can fix the TEST1 by adding a test to check equality of symbols:

def getTypeArgs(t: Type, fromType: Type): List[Type] = {
  if (t.erasure.typeSymbol == fromType.typeSymbol)
    t.typeArgs
  else
    internal
      .thisType(t.dealias.typeSymbol)
      .baseType(fromType.typeSymbol.asClass)
      .typeArgs
}

I don't know how to make TEST3 work.

To-om
  • 75
  • 6
  • What would be the goal? Seems like an XY question – cchantep Sep 25 '18 at 10:14
  • I have several use cases in the scala macros I'm writting. For example, I've a builder of something: `class Builder[FROM, TO] extends (FROM => TO)`. From a builder type, my macro needs to identify the concrete type `FROM` and extract its fields (from primaryConstructor for a case class). My macro must work even if I give to it a subclass of Builder. – To-om Sep 25 '18 at 11:27
  • That still doesn't explain the goal, still XY – cchantep Sep 25 '18 at 11:38
  • The goal is pretty simple. Given a type, I want to check if the type is a `Builder[_, _]` (easily done by `<:<`) and if it is the case, get the type parameters of Builder, even if the input type is a sub type of Builder. – To-om Sep 25 '18 at 12:31
  • That's not a goal, but a mean – cchantep Sep 25 '18 at 15:28
  • Well, my final goal, if it is what you really want is to convert a set of classes into GraphQL schema (using Sangria). Input classes can be a case class, an Iterator or other kind of generic classes. I could do all the job using implicits but a missing implicit is sometimes hard to debug and error message could be obscured. So I decided to use macros. When I get a type I need to know if it is an Iterator (or other generic class) and what is the type parameter. With sub classes of generic class, type parameters of the super class are not directly accessible. – To-om Sep 25 '18 at 16:10

1 Answers1

1

The solution is in fact pretty simple:

def getTypeArgs(t: Type, fromType: Type): List[Type] = {
    t.baseType(fromType.typeSymbol).typeArgs
}

This function passes all the tests.

To-om
  • 75
  • 6