How to get around erased types in pattern match?

How to get around erased types in pattern match?

Example code:

trait RouteRequest
trait RouteResponse

case class Request() extends RouteRequest
case class Response(name: String) extends RouteResponse

type Disparcher[Req <: RouteRequest, Resp <: RouteResponse] = Req => Resp 
type DisparcherAsync[Req <: RouteRequest, Resp <: RouteResponse] = (Req, Resp => Unit) => Unit 

trait ControllerBase

trait Controller1[Req <: RouteRequest, Resp <: RouteResponse] extends ControllerBase:
  def dispatch(r: Req): Resp

trait Controller2[Req <: RouteRequest, Resp <: RouteResponse] extends ControllerBase:
  def dispatch(r: Req): Resp

class ControllerA extends Controller1[Request, Response]:
  def dispatch(r: Request): Response = Response("Controller A")

class ControllerB extends Controller2[Request, Response]:
  def dispatch(r: Request): Response = Response("Controller B")

type Handlers[Req <: RouteRequest, Resp <: RouteResponse] = 
  Controller1[Req, Resp] | Controller2[Req, Resp] | Disparcher[Req, Resp] | DisparcherAsync[Req, Resp]

def handle[Req <: RouteRequest, Resp <: RouteResponse](c: Handlers[Req, Resp]): Unit = 

  val req = Request().asInstanceOf[Req]

  c match 
    case c1: Controller1[_, _] =>
      val ct = c1.asInstanceOf[Controller1[Req, Resp]]
      println(ct.dispatch(req))
    case c1: Controller2[_, _] =>
      val ct = c1.asInstanceOf[Controller2[Req, Resp]]
      println(ct.dispatch(req))
    case c1: Disparcher[_, _] =>
      val cb = c1.asInstanceOf[Disparcher[Req, Resp]]
      println(cb(req))
    case c1: DisparcherAsync[_, _] =>
      val cb = c1.asInstanceOf[DisparcherAsync[Req, Resp]]
      cb(req, r => println(r))

handle(new ControllerA())
handle(new ControllerB())

handle {
  (req: Request) => Response("callback")
}

handle {
  (req: Request, f: (Response) => Unit) => 
    f(Response("callback async"))
}

The code work as fine, but the compiler emit a warning with this message on this case case c: DisparcherAsync[_, _] :

the type test for Playground.DisparcherAsync[, ²] @$4 @$3 cannot be checked at runtime because its type arguments can’t be determined from Playground.ControllerBase

where: _ is a type in method handle with bounds <: Playground.RouteRequest

  •      _² is a type in method handle with bounds <: Playground.RouteResponse*
    

Note that I need to cast the types. Is there any way to get around this situation? I saw something about the TestType class, but I didn’t understand how to fit it into my code.

source code