我try 使用从相同父类继承的2个类(不是这些类的实例)作为WeakMap的键.这两个派生类的构造函数具有不同的签名.

因此,我的代码如下所示:

class Base{
  constructor(args:{}){}
}

class A extends Base{
  constructor(args:{a:string}){ 
    super(args)
   }
}

class B extends Base{
  constructor(args:{b:number}){ 
    super(args)
   }
}

const myMap:WeakMap<new(args:object)=>Base, number> = new WeakMap([
  // [Base, 0],   I will explain after why there is that commented line.
  [A, 1],
  [B, 2]
])

输出结果为:

  No overload matches this call.
  Overload 1 of 2, '(iterable: Iterable<readonly [typeof A, number]>): WeakMap<typeof A, number>', gave the following error.
    Argument of type '([typeof A, number] | [typeof B, number])[]' is not assignable to parameter of type 'Iterable<readonly [typeof A, number]>'.
      The types returned by '[Symbol.iterator]().next(...)' are incompatible between these types.
        Type 'IteratorResult<[typeof A, number] | [typeof B, number], any>' is not assignable to type 'IteratorResult<readonly [typeof A, number], any>'.
          Type 'IteratorYieldResult<[typeof A, number] | [typeof B, number]>' is not assignable to type 'IteratorResult<readonly [typeof A, number], any>'.
            Type 'IteratorYieldResult<[typeof A, number] | [typeof B, number]>' is not assignable to type 'IteratorYieldResult<readonly [typeof A, number]>'.
              Type '[typeof A, number] | [typeof B, number]' is not assignable to type 'readonly [typeof A, number]'.
                Type '[typeof B, number]' is not assignable to type 'readonly [typeof A, number]'.
                  Type at position 0 in source is not compatible with type at position 0 in target.
                    Type 'typeof B' is not assignable to type 'typeof A'.
                      Types of construct signatures are incompatible.
                        Type 'new (args: { a: number; }) => B' is not assignable to type 'new (args: { a: string; }) => A'.
                          Types of parameters 'args' and 'args' are incompatible.
                            Type '{ a: string; }' is not assignable to type '{ a: number; }'.
                              Types of property 'a' are incompatible.
                                Type 'string' is not assignable to type 'number'.
  Overload 2 of 2, '(entries?: readonly (readonly [typeof A, number])[] | null | undefined): WeakMap<typeof A, number>', gave the following error.
    Type 'typeof B' is not assignable to type 'typeof A'.
Argument of type 'typeof A' is not assignable to parameter of type 'new (args: object) => Base'.
  Types of construct signatures are incompatible.
    Type 'new (args: { a: string; }) => A' is not assignable to type 'new (args: object) => Base'.
      Types of parameters 'args' and 'args' are incompatible.
        Property 'a' is missing in type '{}' but required in type '{ a: string; }'.

在我的代码中,我注释了一行:

const myMap:WeakMap<new(args:object)=>Base, number> = new WeakMap([
  // [Base, 0],
  [A, 1],
  [B, 2]
])

如果我取消对该行的注释,则不会出现错误.至少在我不使用我的 map ^^‘的时候没有错误.如果我调用Map的任何方法,比如myMap.get(A),我会得到一个错误:

Argument of type 'typeof A' is not assignable to parameter of type 'new (args: object) => Base'.
  Types of construct signatures are incompatible.
    Type 'new (args: { a: string; }) => A' is not assignable to type 'new (args: object) => Base'.
      Types of parameters 'args' and 'args' are incompatible.
        Property 'a' is missing in type '{}' but required in type '{ a: string; }'.

有什么诀窍?我的意思是类只是用作映射中的键.我不在乎它的签名.

UPDATE : Add a third sub class with a signature even more different :

会员jcalz给了我一个解决这个问题的方案(阅读第一条 comments ).它看起来很棒,但我想知道是否有更通用的解决方案. 索引中,只有当我所有子类都使用单个参数时,才能使用解决方案(使用Never关键字).但是,如果我有一个类,它的构造函数比其他类使用了更多的参数,该怎么办呢?因此,此时,我的代码如下所示:

class Base {
  constructor(args: {}) { }
}

class A extends Base {
  constructor(args: { a: string }) {
    super(args)
  }
}

class B extends Base {
  constructor(args: { b: number }) {
    super(args)
  }
}

class C extends Base{
  constructor(args:{}, other:number){
    super(args)
  }
}

有没有办法做到这样的事情:

const myMap = new WeakMap<any-subClass-of-Base, number>([
  [A, 1],
  [B, 2],
  [C, 3]
])

我知道我可以使用更多的泛型关键字,比如:

const myMap = new WeakMap<Function, number>([
  [A, 1],
  [B, 2],
  [C, 3]
])

或仅与任何一种:

const myMap = new WeakMap<any, number>([
  [A, 1],
  [B, 2],
  [C, 3]
])

但两者都完全忽略了这样一个事实,即我预计只能用作继承Base的关键类.

推荐答案

你遇到了两个问题.


首先,与Map构造函数一样,WeakMap构造函数的类型不允许您向其传递heterogeneous array literal.一旦键或值类型被视为union,编译器就会拒绝它,并显示您看到的错误.这被认为是打字脚本中的错误,并被提交到microsoft/TypeScript#39133号.除非这个问题得到解决,否则您可以通过更改如下 struct 来解决此问题

const x: Map<K, V> = new Map([[⋯]]);

const x = new Map<K, V>([[⋯]]);

That is, instead of calling the construc至r and hoping the compiler will infer the K and V generic type arguments, you manually specify them.


The next is that new (args: object) => Base does not mean "anything that constructs instances assignable 至 Base". It specifically means "anything that allows you 至 call it like new c至r(args) where args is any non-primitive object the caller wants, and which constructs instances assignable 至 Base. Neither A nor B nor C allow you 至 call them that way, so you will still get an error.

Since your use case apparently has nothing 至 do with actually calling the construc至rs (at least not when putting it in至 or getting it out of the map), you don't really care what arguments it accepts. Therefore you're looking for the most permissive construc至r type possible. Because of contravariance of function types in their argument types, that means you are looking for the most restrictive parameter types possible. Since the never type is the most restrictive type (nothing is assignable 至 it), a function whose argument list is of type never is the most permissive function type (every function is assignable 至 it, at least when it comes 至 arguments).

这意味着您希望将new (...args: never) => Base而不是new (args: object) => Base作为 map 的键类型.

Note that if you were 至 try 至 enumerate the keys of myMap, you'd get a bunch of values of type new (...args: never) => Base, and that type is essentially impossible 至 use as a construc至r... because the parameter type is so restrictive. But again, you don't plan 至 do that... as map keys they are just being used as-is, not as construc至rs.


让我们来测试一下.如果我们解决了这两个问题,我们得到:

const myMap = new WeakMap<new (...args: never) => Base, number>([
  [A, 1],
  [B, 2],
  [C, 3]
])

myMap.get(A);
myMap.get(B);
myMap.get(C);

现在可以按预期进行编辑.

Playground link 至 code

Typescript相关问答推荐

将对象属性路径描述为字符串数组的类型

使用React处理Websocket数据

SortKey类型不起作用,尽管它使用的单个类型工作正常<>'

Redux—Toolkit查询仅在不为空的情况下添加参数

将对象属性转换为InstanceType或原样的强类型方法

如何设置绝对路径和相对路径的类型?

如何在LucideAngular 添加自定义图标以及如何使用它们

是否有一个版本的联合类型递归地使属性只出现在一个可选选项中?

我可以以这样一种方式键入对象,只允许它的键作为它的值吗?

如何在Reaction Query Builder中添加其他字段?

有没有办法防止类型交集绕过联合类型限制?

如何静态键入返回数组1到n的函数

在REACT查询中获取未定义的isLoading态

基于闭包类型缩小泛型类型脚本函数的范围

Vite+Chrome扩展 list v3-;不能在模块外使用import语句;对于inpage脚本

如何避免TS2322;类型any不可分配给类型never;使用索引访问时

如何使用 Angular 确定给定值是否与应用程序中特定单选按钮的值匹配?

元素隐式具有any类型,因为string类型的表达式不能用于索引类型{Categories: Element;管理员:元素; }'

如果父级被删除或删除,如何自动删除子级?

基于泛型的柯里化函数的条件参数