Typescript泛型未按预期进行约束

yrwegjxp  于 2023-06-07  发布在  TypeScript
关注(0)|答案(1)|浏览(280)

在下面的TypeScript中,我得到了一些意想不到的东西:

class BaseNode<C> {
  id = ''
}
class ContainerClass<T> {
  nodes: Record<string, BaseNode<T>> = {};

  addNode (node: BaseNode<T>) {
    this.nodes[node.id] = node;
  }
}
class ContextOne {
  getLabel() { return 'one' }
}
class ContextTwo {
  getVal() { return 2 }
}

class GenderNode extends BaseNode<ContextOne> {}
class HeightNode extends BaseNode<ContextTwo> {}

const container = new ContainerClass<ContextOne>()
const genderNode = new GenderNode()
const heightNode = new HeightNode()
container.addNode(genderNode)
container.addNode(heightNode) // THIS WORKS, BUT SHOULD NOT

container的类型是ContainerClass<ContextOne>,这意味着它的addNode函数应该被限制为addNode(node: BaseNode<ContextOne>)类型。但是,我传入heightNode = BaseNode<ContextTwo>
因此,container.addNode()函数只接受与容器使用相同上下文的节点示例。

iqjalb3h

iqjalb3h1#

TypeScript的类型系统是结构化的,而不是nominal。在TypeScript中,类型通过它们的 * 形状 * 或 * 结构 * 进行比较。因此,具有相同成员的两个对象类型被视为同一类型,即使它们具有不同的名称或在不同的位置声明。
在示例代码中,BaseNode<C>的类型为{id: string},它在结构上完全独立于generic类型参数C。所以BaseNode<A>BaseNode<B>都是{id: string},因此它们是相同的,不管AB是什么。这将导致编译器接受所有具有不同名称的事物作为同一类型。参见相关的TS常见问题解答条目。
如果您希望GenderNodeHeightNode被视为不同的类型,那么BaseNode<C>应该在结构上依赖于C。有很多方法可以做到这一点;您应该做最适合您的用例的任何事情。但最简单的方法是将C类型的成员添加到类型中:

class BaseNode<C> {
  id = ''
  declare c: C // <-- 
}

一旦你这样做了,你会看到预期的错误:

container.addNode(genderNode) // okay
container.addNode(heightNode) // error

Playground链接到代码

相关问题