microsoft / microsoft/TypeScript

Refinement type inference quirk with implicitly typed `let x`

Open
#56,097 5 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Bug Domain: check: Control Flow Help Wanted
Dominant language
Go
Stars
111k
Forks
14.3k
Avg merge
1d 19h
Merged PRs (30d)
117

Description

🔎 Search Terms

refinement narrow let any

🕗 Version & Regression Information
  • This changed between versions 3.9.7 and 4.0.5; the behavior is present in every version from 4.0.5 to 5.3 nightly; I skimmed the whole FAQ document and didn't find anything relevant
⏯ Playground Link

https://www.typescriptlang.org/play?ts=5.3.0-dev.20231013#code/C4TwDgpgBAsiAq5oF4oG8qkgLigcgEE8oBfKAH3UyVzwCFiSBuAKBYDMBXAOwGNgAlgHtuUdgAoAhgCcA5rjiJIASnQsoGqABsIwKAA9Wmg1FQzZRzQPZRx+gHRYUyVITyq06zWQhaAztDWtg5Opi74DB5eGj7+0J7Gxjp6APq43BAAbhDSpgaWMSwkLEA

💻 Code
type MyType = { type: 'A' } | { type: 'B' };

function f(arg: MyType) {
    let x;
    x = arg;
    if (x.type === 'A') {
    } else if (x.type === 'B') {
    } else {
        let _: never = x;
    }
}
🙁 Actual behavior
error TS2322: Type '{ type: "B"; }' is not assignable to type 'never'.

9         let _: never = x;
              ~

This is wrong for two reasons:

  • In the world of perfect type inference, x should be narrowed to never at this point
  • Even acknowledging that there could be some practical limitations, the behavior is not consistent.
    image
    First conditional successfully eliminates the "A" variant:
    image
    But second conditional fails to eliminate the "B" variant:
    image
🙂 Expected behavior

Ideally, this shouldn't be an error at all.

However, given that control-flow-based type inference has its limitations, perhaps some kind of spurious error is inevitable. In that case, it probably should be more like Type 'MyType' is not assignable to type 'never' rather than Type '{ type: "B" }' is not assignable to type 'never'.

Additional information about the issue

Practical impact of this issue is small. There is an easy workaround of adding an explicit type annotation (let x: MyType;).

However, this asymmetry, that the last conditional fails at narrowing when exactly the same conditional before that succeeds, bothers me. I'm reporting this because it could be a symptom of a more general implementation defect.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the TypeScript Playground example and compare the behavior across the reported versions. Investigate control-flow-based type inference and narrowing for an implicitly typed let assigned from MyType; done means the final else branch narrows consistently or produces the agreed diagnostic without breaking the earlier narrowing.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.