Skip to content

[5.0.2 regression] unused type guard result erroneously changing inferred type of variableΒ #53311

@seansfkelley

Description

@seansfkelley

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

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 TypeScriptFix AvailableA PR has been opened for this issueRecent RegressionThis is a new regression just found in the last major/minor version of TypeScript.

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions