为什么 class 属性 的类型缩小检查在分配给变量(别名条件)时不起作用?
Why doesn't a type narrowing check of a class property not work when assigned to a variable (aliased conditions)?
如何在别名条件表达式中缩小 class 的 属性 类型?
Short:在 class 方法中,我想进行类型缩小检查,例如 this._end === null && this._head === null
,但我想首先分配结果在类型缩小 if
子句中使用它之前检查变量。它没有按预期工作。
不短:假设我们有一个队列 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 条件中的 enqueue 函数中写一个检查,那么 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;
}
但代码的描述性较差。
此外,一般来说,我需要说的是 class 的两个属性都通过了 null 检查。具体来说:this._head 和 this._end.
我写了下面的方法:
type QueuePointerKeys = '_head' | '_end';
private _isQueueEmpty(): this is this & { [K in QueuePointerKeys]: null } {
return this._end === null;
}
而且它不起作用。如果放在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;
}
如何使用 class 方法(例如 _isQueueEmpty
进行类型缩小?这类似于 this._end === null && this._head === null
直接用作 if 语句的条件?
Control Flow Analysis of Aliased Conditions and Discriminants 是最近才添加的,它有局限性,包括:
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.
由于 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;
}
}
事实上,有一个解决您的具体案例的未决问题: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。
希望他们能尽快找到解决这些问题的方法
如何在别名条件表达式中缩小 class 的 属性 类型?
Short:在 class 方法中,我想进行类型缩小检查,例如 this._end === null && this._head === null
,但我想首先分配结果在类型缩小 if
子句中使用它之前检查变量。它没有按预期工作。
不短:假设我们有一个队列 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 条件中的 enqueue 函数中写一个检查,那么 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;
}
但代码的描述性较差。
此外,一般来说,我需要说的是 class 的两个属性都通过了 null 检查。具体来说:this._head 和 this._end.
我写了下面的方法:
type QueuePointerKeys = '_head' | '_end';
private _isQueueEmpty(): this is this & { [K in QueuePointerKeys]: null } {
return this._end === null;
}
而且它不起作用。如果放在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;
}
如何使用 class 方法(例如 _isQueueEmpty
进行类型缩小?这类似于 this._end === null && this._head === null
直接用作 if 语句的条件?
Control Flow Analysis of Aliased Conditions and Discriminants 是最近才添加的,它有局限性,包括:
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.
由于 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;
}
}
事实上,有一个解决您的具体案例的未决问题: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。
希望他们能尽快找到解决这些问题的方法