Note: in what follows I'm using short uppercase identifiers for type parameters, as the normal convention. You can replace CN
with component
and CS
with composite
at your pleasure (but I'd recommend against using regular identifiers for generic type parameters)
I'm not sure what your use cases are, but default type parameters don't set constraints. In your code,
class Bar<CN extends Component = StackSegment,
CS extends Composite<CN> = Stack> { // error
}
The type parameter CN
is not required to be StackSegment
, and thus it's possible for Stack
not to meet the constraint of Composite<CN>
. One way to deal with it is to make the composite just Composite<CN>
instead of Stack
:
class Bar<CN extends Component = StackSegment,
CS extends Composite<CN> = Composite<CN>> { // okay
}
If you really want to see the default as Stack
(e.g., if Stack
has some extra methods that just Composite<CN>
doesn't), then you can do this:
class Bar<CN extends Component = StackSegment,
CS extends Composite<CN> = Stack & Composite<CN>> { // okay
}
since Stack & Composite<StackSegment>
is the same structural type as Stack
. But again, I don't know your use case. As of now, you get stuff like:
interface OtherComponent extends Component {
thingy: string;
}
class OtherComposite implements Composite<OtherComponent> {
getChildren(): OtherComponent[] {
throw new Error("Method not implemented.");
}
}
new Bar(); // Bar<StackSegment, Stack> as desired
new Bar<OtherComponent, OtherComposite>(); // also makes sense
// but this is a Bar<OtherComponent, Stack & Composite<OtherComponent>>
new Bar<OtherComponent>(); // wha?
// which is weird.
Do you ever intend to use just one default parameter? If not, maybe there's a better way to represent the generics. But without more use case information I wouldn't know how to advise you.
Good luck.
EDIT: An ugly syntax that works the way I think you want is to specify both types in one parameter, like this:
class Bar<CC extends [Component, Composite<CC[0]>]=[StackSegment, Stack],
CN extends CC[0] = CC[0],
CS extends CC[1] = CC[1]> {
}
In the above, CC
is a two-tuple of types with the constraint you want (the second parameter must be compatible with Composite<>
of the first parameter), and it defaults to the pair [StackSegment, Stack]
as desired. (The CN
and CS
types are just there for convenience so you don't need to use CC[0]
for the component type and CC[1]
for the composite type).
Now the behavior is something like this:
new Bar(); // CN is StackSegment and CS is Stack, as desired
new Bar<[OtherComponent, OtherComposite]>(); // also makes sense
But you can't easily break it like before:
new Bar<[OtherComponent]>(); // error
new Bar<[OtherComponent, Stack]>(); // error
Okay, good luck again!