【问题标题】:TypeScript: Enforce generic type constraint in a nested static classTypeScript:在嵌套的静态类中强制执行泛型类型约束
【发布时间】:2020-05-26 13:25:58
【问题描述】:

为了举例,考虑 TypeScript 3.7.5 中的最小链表类。一个LinkedList<T> 由一个ListNode<T> 链组成,其中类型变量T 在两者中指代相同的类型。还假设您使用LinkedList 中的私有静态字段来隐藏ListNode,因为它是不相关的实现细节。

class LinkedList<T> {
  private head: ??? = null;
  private tail: ??? = null;

  private static ListNode = class ListNode<T> {
    constructor(
      public val: T | null,
      public next: ListNode<T> | null) {}
  };

  append(item: T): this {
    if (!this.tail) {
      this.tail = {val: item, next: null};
      this.head = this.tail;
    } else {
      this.tail.next = {val: item, next: null};
      this.tail = this.tail.next;
    }
    return this;
  };

  remove(): T {
    if (!this.head || this.head.val === null) {
      throw Error();
    } else {
      const t = this.head.val;
      this.head = this.head.next;
      return t;
    }
  }
}

我如何表示上面的类型????它不是List.ListNodeList.ListNode&lt;T&gt;。这不是有效的 TypeScript(至少从 3.7.5 开始)。它也不是InstanceType&lt;typeof List.ListNode&gt;。这是一个有效的类型,但它丢失了泛型参数T,因此无法强制执行封闭类和嵌套类由相同类型参数化的约束。

现在我们通过引入虚拟head 并依靠类型推断来修改类:

class LinkedList<T> {
  private head = LinkedList.makeNode<T>();
  private tail = this.head.next;

  private static makeNode<T>() {
    return new this.ListNode<T>(null, null);
  }

  private static ListNode = class ListNode<T> {
    constructor(
      public val: T | null,
      public next: ListNode<T> | null) {}
  };

  append(item: T): this {
    if (!this.tail) {
      this.head.next = {val: item, next: null};
      this.tail = this.head.next;
    } else {
      this.tail.next = {val: item, next: null};
      this.tail = this.tail.next;
    }
    return this;
  };

  remove(): T {
    if (!this.head.next || this.head.next.val === null) {
      throw Error();
    } else {
      const t = this.head.next.val;
      this.head.next = this.head.next.next;
      return t;
    }
  }
}

给定这段代码,TypeScript 可以证明 remove() 实际上确实返回了 T 的实例。在悬停时,VSC 建议head 的类型是ListNode&lt;T&gt;。如何明确表达这种类型?

【问题讨论】:

  • 在LinkedList里面嵌套ListNode的目的是什么?这条线试图实现什么:private static ListNode = class ListNode&lt;T&gt; { }
  • 请考虑将上述代码转换为minimal reproducible example,可以将其放入the TypeScript playground 之类的独立IDE 中以演示您的问题。应该消除与您的问题无关的任何问题(例如,List 未声明,ListNode 的属性未初始化),以便回答的人可以专注于正确的事情。祝你好运!

标签: typescript types


【解决方案1】:

创建一个字段来存储列表节点的 type 对我来说没有意义。它也不需要是一个类;对象类型很好。我会这样做:[ts playground]

type ListNode<T> = {
  val: T;
  next: ListNode<T> | null;
};

class LinkedList<T> {
  private head: ListNode<T> | null = null;
  private tail: ListNode<T> | null = null;


  append(item: T): this {
    if (!this.tail) {
      this.tail = {val: item, next: null};
      this.head = this.tail;
    } else {
      this.tail.next = {val: item, next: null};
      this.tail = this.tail.next;
    }
    return this;
  };

  remove(): T {
    if (!this.head) {
      throw Error();
    } else {
      const t = this.head.val;
      this.head = this.head.next;
      return t;
    }
  }
}

【讨论】:

  • 感谢您的建议!我感兴趣的不是如何写一个清单。我对如何访问嵌套在静态字段中的泛型类的类型特别感兴趣。鉴于在第二个代码中 sn -p tsc 似乎能够推断出 head 的类型,如果这种类型在 TS 中无法表达,那就很奇怪了。你不同意吗?
  • 嗯,你能表达ListNode本身的类型吗?这里会发生什么:static ListNode: ??? = class ... 首先我对这种嵌套的含义/意图感到困惑。
  • 是的,这几乎是同一个问题。显然,这是一个类型化的表达式,编译器可以推断它的类型。
  • 不太好,我认为 :)
  • 但足以知道remove() 在第二个示例中返回T
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-01-21
  • 2019-06-27
  • 2017-09-23
  • 2018-03-05
  • 2020-05-30
  • 1970-01-01
相关资源
最近更新 更多