Microsoft/TypeScript

Inverted union type narrowing was broken in 4.9

Open

#58,861 opened on Jun 14, 2024

View on GitHub
 (2 comments) (0 reactions) (0 assignees)TypeScript (6,726 forks)batch import
BugDomain: check: Control FlowHelp Wanted

Repository metrics

Stars
 (48,455 stars)
PR merge metrics
 (Avg merge 6d 17h) (9 merged PRs in 30d)

Description

🔎 Search Terms

"union type", "narrowing", "inverted", "4.9"

🕗 Version & Regression Information

⏯ Playground Link

Playground link

💻 Code

interface Foo { foo: string; }

interface Bar { bar: string; }

interface Baz { baz: string; }

function myFunction(input: Foo | Bar | Baz) {
    const isBaz = 'baz' in input;
    const isBar = 
      //'bar' in input; // This works to narrow `bar` when true and narrow `foo` in the else block
      !('foo' in input) && !isBaz; // This works to narrow `bar` when true but does not narrow `foo` in the else block
    let x: string;
    if (isBaz) {
      x = input.baz;
    } else if (isBar) {
      x = input.bar;
    } else {
      // In 4.8.4 and earlier, `input` is narrowed to `Foo`
      // In 4.9.5 and later, `input` is narrowed to `Foo | ((Foo | Bar) & Record<"baz", unknown>)`
      x = input.foo;
    }
}

🙁 Actual behavior

In 4.9.5 and later, in the else block, input.foo incorrectly narrows and errors

🙂 Expected behavior

In 4.8.4 and earlier, in the else block, input.foo correctly narrows

Additional information about the issue

No response

Contributor guide