You can try to narrow return type in child traits
trait Hello[T] extends Foo[T] { def getA: Decoder[T] def getB: Left[Type1, Type2] } trait World[T] extends Foo[T] { def getA: Decoder[T] def getB: Right[Type1, Type2] }
Or you can try to introduce a type member
sealed trait Foo[T] { def getA: Decoder[T] type Out def getB: Out } trait Hello[T] extends Foo[T] { def getA: Decoder[T] type Out = Type1 def getB: Type1 } trait World[T] extends Foo[T] { def getA: Decoder[T] type Out = Type2 def getB: Type2 }
Or you can try a type class
sealed trait Foo[T] { type This <: Foo[T] def getA: Decoder[T] def getB(implicit tc: TC[T, This]): tc.Out } trait Hello[T] extends Foo[T] { type This <: Hello[T] def getA: Decoder[T] } trait World[T] extends Foo[T] { type This <: World[T] def getA: Decoder[T] } trait TC[T, F <: Foo[T]] { type Out } object TC { implicit def hello[T, F <: Hello[T]]: TC[T, F] { type Out = Type1 } = null implicit def world[T, F <: World[T]]: TC[T, F] { type Out = Type2 } = null }