简体   繁体   English

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

[英]Why doesn't a type narrowing check of a class property not work when assigned to a variable (aliased conditions)?

How can I narrow the type of property of a class in an aliased conditional expressions?如何在别名条件表达式中缩小 class 的属性类型?

Short : In a class method I want to do a type narrowing check such as this._end === null && this._head === null , but I want to first assign the result of this check to a variable before using it in a type narrowing if clause.简短:在 class 方法中,我想做一个类型缩小检查,例如this._end === null && this._head === null之前的变量,但我想先在这个检查结果中使用它一种缩小if子句的类型。 It's not working as expected.它没有按预期工作。

Not short : Let's say we have a Queue class:不短:假设我们有一个队列class:

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

If you write a check in the enqueue function directly in the if condition, then TypeScript understands that this._end is not null :如果在if条件下直接在enqueue function中写check,那么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;
}

But the code turns out to be less descriptive.但是代码的描述性较差。

Moreover, in general, I need to say that both properties of the class passed the null check.此外,总的来说,我需要说 class 的两个属性都通过了null检查。 Specifically: this._head and this._end .具体来说: this._headthis._end

I wrote the following method:我写了以下方法:

type QueuePointerKeys = '_head' | '_end';

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

And it doesn't work.它不起作用。 If you put it in the enqueue method, then:如果你把它放在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;
}

How do I use a class method such as _isQueueEmpty for type narrowing?如何使用 class 方法(例如_isQueueEmpty进行类型缩小? That works similarly to this._end === null && this._head === null used directly as the condition of the if statement?这类似于this._end === null && this._head === null直接用作if语句的条件?

Control Flow Analysis of Aliased Conditions and Discriminants was only recently added, and it has limitations, including: 混叠条件和判别式的控制流分析是最近才添加的,它有局限性,包括:

Narrowing through indirect references occurs only when the conditional expression or discriminant property access is declared in a const variable declaration with no type annotation, and the reference being narrowed is a const variable, a readonly property, or a parameter for which there are no assignments in the function body.仅当条件表达式或判别属性访问在没有类型注释的 const 变量声明中声明并且被缩小的引用是 const 变量、只读属性或在function 机身。

Since this._end is not a readonly property, it's not going to work for you.由于this._end不是只读属性,因此它对您不起作用。 I confirmed that this is your problem by assigning this._end to a local const variable, and then using it for the condition as well as the setLink call:我通过将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;
  }

}

In fact, there is an open issue addressing your specific case: Control flow analysis of aliased conditions is not able to narrow object properties #46412 , as well as Allow non-readonly properties to be used in aliased conditional expressions #44972 .事实上,有一个针对您的具体情况的未解决问题:别名条件的控制流分析无法缩小 object 属性 #46412以及Allow non-readonly properties to be used in aliased conditional expressions #44972

Let's hope they can find solutions for these soonish让我们希望他们能尽快找到解决方案

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM