首页 > 解决方案 > TypeScript:在嵌套的静态类中强制执行泛型类型约束

问题描述

作为示例,考虑 TypeScript 3.7.5 中的最小链表类。ALinkedList<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<T>。这不是有效的 TypeScript(至少从 3.7.5 开始)。它也不是InstanceType<typeof List.ListNode>。这是一个有效的类型,但它失去了泛型参数T,因此无法强制执行封闭类和嵌套类由相同类型参数化的约束。

现在我们通过引入一个 dummyhead并依靠类型推断来修改类:

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 建议类型headListNode<T>. 如何明确表达这种类型?

标签: typescripttypes

解决方案


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

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;
    }
  }
}

推荐阅读