为什么在分配给变量(别名条件)时,类属性的类型缩小检查不起作用?

Max*_*rin 5 javascript typescript type-narrowing typeguards typescript4.0

如何缩小别名条件表达式中类的属性类型?

简而言之:在类方法中,我想要执行类型缩小检查,例如this._end === null && this._head === null,但我想首先将此检查的结果分配给变量,然后再在类型缩小if子句中使用它。它没有按预期工作。

不短:假设我们有一个Queue类:

class Queue {
  private _length: number = 0;
  private _head: Node | null = null;
  private _end: Node | null = null;

  public enqueue(node: Node): boolean {
    this._length += 1;

    const isQueueEmpty = this._end === null;
    if (isQueueEmpty) {
      this._head = node;
      this._end = node;
      return true;
    }

    this._end.setLink(node); // TypeScript doesn't understand that this._end passed the null check 
                             // in the if statement and writes that this._end can be null. 
    this._end = node;
    return true;
  }
}
Run Code Online (Sandbox Code Playgroud)

如果您直接在if条件中编写入队函数的检查,则 TypeScript 会理解this._end不为null

public enqueue(node: Node): boolean {
  this._length += 1;

  if (this._end === null) {
    this._head = node;
    this._end = node;
    return true;
  }

  this._end.setLink(node); // Now TypeScript understand that this._end passed the null check
  this._end = node;
  return true;
}
Run Code Online (Sandbox Code Playgroud)

但事实证明代码的描述性较差。

而且,一般来说,我需要说的是,该类的两个属性都通过了检查。具体来说:this._headthis._end

我写了以下方法:

type QueuePointerKeys = '_head' | '_end';

private _isQueueEmpty(): this is this & { [K in QueuePointerKeys]: null } {
  return this._end === null;
}
Run Code Online (Sandbox Code Playgroud)

但这不起作用。如果你把它放在enqueue方法中,那么:

public enqueue(node: Node): boolean {
  this._length += 1;

  if (this._isQueueEmpty()) {
    this._head = node;     // TypeScript writes that a variable of type null cannot be assigned the type Node
    this._end = node;      // TypeScript writes that a variable of type null cannot be assigned the type Node
    return true;
  }

  this._end.setLink(node); // TypeScript doesn't understand that this._end passed the null check
                           // in the if statement and writes that this._end can be null.
  this._end = node;
  return true;
}
Run Code Online (Sandbox Code Playgroud)

如何使用类方法(例如_isQueueEmpty类型缩小)?这与直接用作ifthis._end === null && this._head === null语句的条件类似吗?

vas*_*vas 2

别名条件和判别式的控制流分析最近才添加,它有局限性,包括:

仅当条件表达式或判别式属性访问在没有类型注释的 const 变量声明中声明,并且被缩小的引用是 const 变量、只读属性或未对其进行赋值的参数时,才会发生通过间接引用进行缩小的情况函数体。

由于this._end不是只读属性,因此它不会为您工作。this._end我通过分配给本地 const 变量,然后将其用于条件和调用来确认这是您的问题setLink

interface Node {
  setLink(node: Node): void
}

class Queue {
  private _length: number = 0;
  private _head: Node | null = null;
  private _end: Node | null = null;

  public enqueue(node: Node): boolean {
    this._length += 1;

    const end = this._end
    const isQueueEmpty = end === null;

    if (isQueueEmpty) {
      this._head = node;
      this._end = node;
      return true;
    }

    end.setLink(node);
    this._end = node;
    return true;
  }

}
Run Code Online (Sandbox Code Playgroud)

事实上,有一个解决您的具体情况的悬而未决的问题: 别名条件的控制流分析无法缩小对象属性 #46412,以及允许在别名条件表达式中使用非只读属性 #44972

希望他们能尽快找到解决方案