-
Notifications
You must be signed in to change notification settings - Fork 13.2k
Closed
Labels
BugA bug in TypeScriptA bug in TypeScriptFix AvailableA PR has been opened for this issueA PR has been opened for this issueRecent RegressionThis is a new regression just found in the last major/minor version of TypeScript.This is a new regression just found in the last major/minor version of TypeScript.
Milestone
Description
Bug Report
π Search Terms
control flow analysis type guard narrowing widening no-op
π Version & Regression Information
- This changed between versions 4.9.5 and 5.0.2
β― Playground Link
π» Code
type FooNode = {
kind: 'foo';
children: Node[];
};
type BarNode = {
kind: 'bar';
}
type Node = FooNode | BarNode;
type Document = {
kind: 'document';
children: Node[];
};
declare function isNode(node: unknown): node is Node;
declare function isBar(node: Node): node is BarNode;
export function visitNodes<T extends Node>(
node: Document | Node,
predicate: (testNode: Node) => testNode is T,
): void {
// Spooky action at a distance: this no-op statement causes the compiler to reject the assignment
// guarded by the conditional, below. Comment it out to fix the failure.
isNode(node) && predicate(node);
if (!isNode(node) || !isBar(node)) {
// This line should compile fine!
const nodes: Node[] = node.children;
}
}The original code that turned this issue up was a bit more indirect and looked like:
const shouldYield = isNode(node) && predicate(node);
if (shouldYield) {
yield node;
}
const hasChildren = !isNode(node) || !isBar(node);
if (hasChildren) {
yield* yieldAll(node.children);
}I was able to work around the issue by inlining the guards directly into the if rather than using an intermediate value.
π Actual behavior
The assignment const nodes: Node[] = node.children; fails, complaining that
Property 'children' does not exist on type 'FooNode | Document | T'.
Property 'children' does not exist on type 'T'.(2339)
but only if the unused type guard at the beginning of the function is present. Commenting the dead code out fixes the issue, and the compiler correctly narrows the type of node to just FooNode | Document.
π Expected behavior
The unused type guard/dead code has no effect on the inferred type of the value.
Metadata
Metadata
Assignees
Labels
BugA bug in TypeScriptA bug in TypeScriptFix AvailableA PR has been opened for this issueA PR has been opened for this issueRecent RegressionThis is a new regression just found in the last major/minor version of TypeScript.This is a new regression just found in the last major/minor version of TypeScript.