Updated answer
I found an even simpler way, which doesn't rely on structural types. Now, a PageRenderer
implementation gets its OutputType
from the ComponentRenderer
it's defined with.
trait PageRenderer {
type MatchingComponentRenderer <: ComponentRenderer
type OutputType = MatchingComponentRender#OutputType
// ...
Old answer
I eventually solved the problem with a fix inspired by wingedsubmariner's suggestion, but sticking with abstract type members instead of converting to type parameters. I did something like this:
trait PageRenderer {
self =>
type OutputType
type MatchingComponentRenderer = ComponentRenderer { type OutputType = self.OutputType }
// ...
Then, I changed all references of ComponentRenderer
to MatchedComponentRenderer
within PageRenderer
and its overrides. It compiled just fine after that! The structural type annotation effectively unified the OutputType
.