考虑以下伪代码:

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的静态比较函数?

(在任何人发表 comments 之前,请不要提出解决方案,我如何才能简化整个事情并完全避免这个问题.显然,上面是一个类似于MCVE的例子来说明这个问题,而不是我正在处理的完整代码.)

推荐答案

理想情况下,您应该为您的X超类提供一个static方法,该方法只允许它对类的实例进行操作.但这需要使用polymorphic this type,而TypeScrip目前不支持static方法中的此类类型.请参阅microsoft/TypeScript#5863处的长期开放功能请求.除非这一点得到实施,否则我们将需要绕过它.

常见的解决方法之一是将方法设置为generic,并给它一个表示类似约束的this parameter.因此,静态方法只能在具有正确形状的类上调用.

因此,假设我们只想被允许在具有适当类型的compare方法的类构造函数上调用sort().那种类型可能是

interface Comparator<T> {
  compare: (this: void, a: T, b: T) => number;
}

这意味着我们要求类构造函数对于大约T个值为Comparator<T>.(请注意void this上下文,其中compare必须是可调用的,而不是绑定到对象;如果我们愿意,可以放松这一点,但希望这已经足够好了.)然后,我们将在X上编写sort(),如下所示:

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

看上go 不错.支持您想要的呼叫,拒绝不想要的呼叫.您只能对包含Z个实例的数组调用Z.sort(),并且根本不允许直接调用X.sort(),因为X根本不是Comparator.

Playground link to code

Javascript相关问答推荐

有没有方法在Angular中的bowser选项卡之间移动HTML?

如何在我的Web应用程序中使用JavaScript显示和隐藏喜欢/不喜欢按钮?

如何在react + react路由域名中使用DeliverBrowserRouter?

获取最接近的父项(即自定义元素)

我的YouTube视频没有以html形式显示,以获取免费加密信号

Mongodb拥有5亿个文档,我想根据JavaScript驱动程序中的两个字段使用regEx进行搜索,而不是模式

如何通过onClick为一组按钮分配功能;

成帧器运动中的运动组件为何以收件箱开始?

GrapeJS -如何保存和加载自定义页面

为什么这个JS模块在TypeScript中使用默认属性导入?""

如何在模块层面提供服务?

如何从网站www.example.com获取表与Cheerio谷歌应用程序脚本

我可以使用使用node.js创建的本地主机来存储我网站上提交的所有数据吗?没有SQL或任何数据库.只有HTML语言

使用Nuxt Apollo在Piniastore 中获取产品细节

如何限制显示在分页中的可见页面的数量

在渲染turbo流之后滚动到元素

将Auth0用户对象存储在nextjs类型脚本的Reaction上下文中

是否可以在不更改组件标识的情况下换出Reaction组件定义(以维护状态/引用等)?如果是这样的话,是如何做到的呢?

ComponentWillReceiveProps仍在React 18.2.0中工作

如何将字符串拆分成单词并跟踪每个单词的索引(在原始字符串中)?