README.adoc

May 13, 2025 ยท View on GitHub

:testdir: src/test/scala/io/github/gaeljw/typetrees

= TypeTrees :toc:

[link=https://search.maven.org/artifact/io.github.gaeljw/typetrees_3] image::https://img.shields.io/maven-central/v/io.github.gaeljw/typetrees_3.svg[] image::https://github.com/gaeljw/typetrees/workflows/Scala%20CI/badge.svg[]

WARNING: (2025-05) This library served as a prototype and should not used in any real project. It is unmaintained. More robust projects (or even vanilla Scala depending on the context) exist to achieve the same.

This library is intended to solve the use case of getting the erased types of a (generic) parameter. Something which was possible in Scala 2 with +TypeTag+ and is now a bit more complex with Scala 3.

== Get it

.sbt [source]

libraryDependencies += "io.github.gaeljw" %% "typetrees" % typetreesVersion

.Maven [source,xml]

io.github.gaeljw typetrees_${scala.version} ${typetrees.version} ----

== Usage

Two implementations are available:

  • using implicits (given s)
  • using macro

|=== |Implementation |Pros ๐Ÿ‘ |Cons ๐Ÿ‘Ž

|Implicits |Easier |Only supports up to 4 generic parameters (i.e. a type T[A, B, C, D])

|Macro |Can handle all cases |Requires to be used in inline method |===

=== Using implicits

[source,scala]

import io.github.gaeljw.typetrees.TypeTree import io.github.gaeljw.typetrees.TypeTreeTag

import scala.reflect.ClassTag

val tag: TypeTreeTag = summon[TypeTree[T]].tag // <1>

val classTag: ClassTag[] = tag.self // <2> val actualClass: Class[] = classTag.runtimeClass

val typeParameters: List[TypeTreeTag] = tag.args // <3>

<1> Get a +TypeTreeTag+ for a generic type +T+ <2> Get a +ClassTag+ for this type <3> Get a +TypeTreeTag+ for each type parameters if any

==== Examples

The main usage is as follows:

[source,scala]

def someGenericMethod[T](t: T)(using typeTree: TypeTree[T]): String = { val tag: TypeTreeTag = typeTree.tag s"I have been called with a parameter of type $tag" }

someGenericMethod(MapString, Int) // Gives: I have been called with a parameter of type TypeTreeTag(scala.collection.immutable.Map,List(TypeTreeTag(java.lang.String,List()), TypeTreeTag(Int,List())))

Or with context bounds:

[source,scala]

def someGenericMethod[T : TypeTree](t: T): String = { val tag: TypeTreeTag = summon[TypeTree[T]].tag s"I have been called with a parameter of type $tag" }

You can find more examples in our link:{testdir}/TestBehaviorImplicits.scala[tests].

=== Using macro

[source,scala]

import io.github.gaeljw.typetrees.TypeTreeTag import io.github.gaeljw.typetrees.TypeTreeTagMacros.typeTreeTag

import scala.reflect.ClassTag

val tag: TypeTreeTag = typeTreeTag[T] // <1>

val classTag: ClassTag[] = tag.self // <2> val actualClass: Class[] = classTag.runtimeClass

val typeParameters: List[TypeTreeTag] = tag.args // <3>

<1> Get a +TypeTreeTag+ for a generic type +T+ <2> Get a +ClassTag+ for this type <3> Get a +TypeTreeTag+ for each type parameters if any

==== Examples

The main usage is as follows, within a generic +inline+ method:

[source,scala]

inline def someGenericMethod[T](t: T): String = { val tag: TypeTreeTag = typeTreeTag[T] s"I have been called with a parameter of type $tag" }

someGenericMethod(MapString, Int) // Gives: I have been called with a parameter of type TypeTreeTag(scala.collection.immutable.Map,List(TypeTreeTag(java.lang.String,List()), TypeTreeTag(Int,List())))

Or:

[source,scala]

inline def someGenericMapMethod[T <: Map[,]](map: T): String = { val mapTag: TypeTreeTag = typeTreeTag[T] val keyTag: TypeTreeTag = mapTag.args(0) val valueTag: TypeTreeTag = mapTag.args(1) s"I have been called with a Map where key is of type keyTagandvalueisoftypekeyTag and value is of type valueTag" }

It can also be applied to non generic types: in such case it doesn't need to be part of a +inline def+ but you probably can just use regular ClassTag then.

You can find more examples in our link:{testdir}/TestBehaviorMacro.scala[tests].