考虑以下伪代码:
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
排序函数应利用用于调用它的类型的相应比较函数。这是行不通的,因为 T 不能这样使用。如何更改最后的通用函数以便我可以使用 Y 的静态比较函数?
(在任何人发表评论之前,请不要建议我如何简化整个事情并完全避免问题的解决方案。显然,上面是一个 MCVE 式的示例来说明问题,而不是我正在处理的完整代码。 )
理想情况下,您应该为您的
X
超类提供一个 static
方法,该方法仅允许其在该类的实例上进行操作。但这需要使用 多态 this
类型,而 TypeScript 目前不支持 static
方法中的此类类型。请参阅 microsoft/TypeScript#5863 中长期存在的开放功能请求。直到并且除非它得到实施,我们将需要解决它。
常见的解决方法之一是使方法通用,并为其提供代表类似约束的
this
参数。因此静态方法只能在正确形状的类上调用。
因此,假设我们只想被允许在具有适当类型的
sort()
方法的类构造函数上调用 compare
。这种类型可能是
interface Comparator<T> {
compare: (this: void, a: T, b: T) => number;
}
意味着我们要求类构造函数对于某些
Comparator<T>
来说是 T
。 (注意 void
this
上下文表示 compare
必须可调用,无需绑定到对象;如果我们愿意,可以放宽,但希望这足够好。)然后我们在 上写
sort()
X
像这样:
abstract class X {
static sort<T extends X>(this: Comparator<T>, items: T[]) {
return items.sort(this.compare);
}
}
编译干净;让我们测试一下:
class Y extends X {
id: number = 0;
static compare(a: Y, b: Y) {
return a.id - b.id;
}
}
class Z extends X {
name: string = "";
static compare(a: Z, b: Z) {
return a.name.localeCompare(b.name);
}
}
Y.sort([new Y(), new Y()]); // okay
Z.sort([new Z(), new Z()]); // okay
Z.sort([new Y(), new Z()]); // error
// ---> ~~~~~~~ Y is not Z
X.sort([new Y(), new Z()]); // error
// <-- compare is missing from X
看起来不错。支持您想要的呼叫,拒绝不需要的呼叫。您只能在
Z.sort()
实例数组上调用 Z
,并且根本不允许直接调用 X.sort()
,因为 X
根本不是 Comparator
。