Consider the following pseudo code:
abstract class X {}
class Y extends X {
static compare(a: Y, b: Y) {
return a.id - b.id;
}
}
class Z extends X {
static compare(a: Z, b: Z) {
return a.name.localCompare(b.name);
}
}
function sort<T extends X>(items: T[]) {
return items.sort(T.compare); // <-- illegal
}
Y.sort([new Y(), new Y()]); // <-- should use Ys compare
Z.sort([new Z(), new Z()]); // <-- should use Zs compare
The sort function should utilize the respective compare function of the type used to call it. This doesn’t work because T can’t be used like that. How can I change the generic function at the end so I can use the static compare function of Y?
(And before anyone comments, please do not suggest solutions how I could simplify this whole thing and avoid the issue altogether. Obviously, the above is an MCVE-esque example to illustrate the problem and not the full code I’m dealing with.)
2
Answers
Ideally you’d give your
X
superclass astatic
method that only allows it to operate on instances of the class. But that would require using a polymorphicthis
type, and TypeScript currently doesn’t support such types insidestatic
methods. See the longstanding open feature request at microsoft/TypeScript#5863. Until and unless that’s ever implemented, we’ll need to work around it.One of the common workarounds is to make the method generic and give it a
this
parameter that represents a similar constraint. So the static method will only be callable on a class of the right shape.So let’s say that we only want to be allowed to call
sort()
on a class constructor which has acompare
method of the appropriate type. That type might bemeaning that we require the class constructor to be a
Comparator<T>
for someT
. (Note thevoid
this
context which sayscompare
must be callable without being bound to an object; that can be relaxed if we want, but hopefully that’s good enough.) Then we’d writesort()
onX
like this:That compiles cleanly; let’s test it out:
Looks good. Your desired calls are supported, and undesirable calls are rejected. You’re only allowed to call
Z.sort()
on an array ofZ
instances, and you’re not allowed to callX.sort()
directly at all becauseX
is not aComparator
at all.Playground link to code
Playground link