是否可以这样输入Map<K, V>,其中值的类型将取决于键的类型,而不在创建映射时指定键的类型?

示例:

abstract class BaseA { a() {} }
class ConcreteA1 extends BaseA { a1() {} }
class ConcreteA2 extends BaseA { a2() {} }
abstract class BaseB { b() {} }
class ConcreteB extends BaseB { b1() {} }

const map = new Map<???, ???>();
map.set(BaseA, ConcreteA1); // should be OK
map.set(BaseA, ConcreteA2); // should be OK
map.set(BaseB, ConcreteB);  // should be OK

map.set(BaseA, ConcreteB); // should error

额外收获:我们也能犯错误吗?

由于BaseA是抽象的,如果可能的话,这也应该是一个错误,因为BaseA不满足new (...args: any[]) => BaseA,因为它是不可实例化的.

map.set(BaseA, BaseA); // should error as a bonus

最新情况:

我需要保存依赖关系实现的映射,比如值是键(基类型)的具体实现(扩展).

推荐答案

TypeScript typings for Map只支持Map<K, V>,其中K是键类型,V是值类型,它们之间没有进一步的关联.因此,如果您只按原样使用Map,就不能让TypeScrip编译器强制执行额外的约束.

根据我对您问题的理解,我理解您的限制是键应该是(可能是抽象的)构造函数类型,并且对于每个键,值应该是兼容类型的构造函数(必须是具体的).由于您的示例代码只使用set()方法,因此使您的示例代码按所需方式运行的最少类型如下:

interface MySpecialMap {
  set<T extends object>(
    k: abstract new (...args: any) => T, 
    v: new (...args: any) => T
  ): this;
}
interface MySpecialMapConstructor {
  new(): MySpecialMap;
}
const MySpecialMap: MySpecialMapConstructor = Map;

在这里,我将MySpecialMap构造函数定义为运行时的Map构造函数,但我为其指定了类型MySpecialMapConstructor,该类型构造MySpecialMap的实例.MySpecialMap接口只有一个方法set(),它在基类实例的类型T中是generic.k参数是T个实例类型的possibly abstract constructor,而v参数是T个实例类型的必须是具体的构造函数.

让我们演示一下它是否按预期工作:

abstract class BaseA { a() { } }
class ConcreteA1 extends BaseA { a1() { } }
class ConcreteA2 extends BaseA { a2() { } }
abstract class BaseB { b() { } }
class ConcreteB extends BaseB { b1() { } }

const map = new MySpecialMap();
map.set(BaseA, ConcreteA1); // ok
map.set(BaseA, ConcreteA2); // ok
map.set(BaseB, ConcreteB);  // ok
map.set(BaseA, ConcreteB); // error
// Property 'a' is missing in type 'ConcreteB' but required in type 'BaseA'
map.set(BaseA, BaseA); // error
// Cannot assign an abstract constructor type to a non-abstract constructor type.

看上go 不错!

在此之后,您可能希望为get()或所需的任何其他方法添加类似的类型.或者,您可能希望更改set()的调用签名,以指定零参数构造函数或要强制实施的任何其他约束.但希望这能让你开始学习.

Playground link to code

Typescript相关问答推荐

如何为父类构造函数中的修饰属性赋值?

为什么tsx不判断名称中有"—"的属性?'

使Typescribe强制所有对象具有相同的 struct ,从两个选项

TypeScript:在作为参数传递给另一个函数的记录中对函数的参数实现类型约束

Next.js错误:不变:页面未生成

在包含泛型的类型脚本中引用递归类型A<;-&B

AngularJS服务不会解析传入的Json响应到管道中的模型

找不到财产的标准方式?

创建Angular 为17的动态形状时出错

基于泛型的条件接口键

如何创建由空格连接的多个字符串的类型

Typescript -返回接口中函数的类型

为什么发送空字段?

TypeScript是否不知道其他函数内部的类型判断?

使用受保护的路由和入门来响应路由

是否有可能不允许在 TypeScript 中设置类属性,但也会抛出运行时错误?

创建通过删除参数来转换函数的对象文字的类型

Typescript 枚举给变量提供了错误的类型?

如何使用 SSR Next 13 清除/重置提取

从已知键推断类型