Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Discriminating a type union results in unexpected possible unsoundness #46730

Closed
denis-sokolov opened this issue Nov 8, 2021 · 5 comments
Closed
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug

Comments

@denis-sokolov
Copy link

denis-sokolov commented Nov 8, 2021

Bug Report

🔎 Search Terms

type guard, property checking, discriminated, narrowing, union, property existence, type assertion,

I’m sorry if this is a duplicate, I could not come up with a clear way to name this behavior and recognize it in the list of issues.

🕗 Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about Common "Bugs" That Aren't Bugs and Type System Behavior

Earliest version tested: 3.3.3333
Last version tested: 4.6.0-dev.20211105

⏯ Playground Link

Playground link with relevant code

💻 Code

type Foo = { id: string } | { id: string; optional: number };
function noOptionalAllowed(param: { id: string; optional?: never }) {}

const b = { id: '', optional: 2 } as Foo;
noOptionalAllowed(b); // Error, as expected
noOptionalAllowed("optional" in b ? b : b); // No error?

🙁 Actual behavior

No error on the last line.

🙂 Expected behavior

Error on the last line. x ? b : b and b should be of the same type and behave the same (ignoring side-effects in x). On the last line of the example the type of b in the “if-true” branch includes the optional field, but the type of the entire expression "optional" in b ? b : b is { id: string } with the optional field missing!

@MartinJohns
Copy link
Contributor

Your variable a is narrowed to the type { id: string; optional: number }, that's why you see the different behaviour. This narrowing is intended. Using the type assertion as Foo you prevent this narrowing.

@denis-sokolov denis-sokolov changed the title Unexpected types in a case of discriminating a type union with type assertion having a difference Discriminating a type union results in unexpected possible unsoundness Nov 8, 2021
@denis-sokolov
Copy link
Author

@MartinJohns, thank you, this explains the case with a. I was misled by the type hint at the declaration line (new issue), but everywhere else it is consistent. This simplifies the issue considerably and leaves only the core of the question in here. Thank you!

@andrewbranch
Copy link
Member

Subtype reduction strikes again. This example looks pretty unfortunate though 😕

@andrewbranch
Copy link
Member

I don’t think there’s anything we can do here. The ternary does subtype reduction on the union of the narrowed type and the unnarrowed type of b (the true and false branch of the conditional), which is { id: string }. Then that is subsequently assignable to { id: string, optional?: never }. This is the same unsoundness as this aliasing: https://www.typescriptlang.org/play?jsx=4&ts=4.1.5#code/CYUwxgNghgTiAEEQBd7NgcxQLngb3ilwGdkYBLAOwwBp4AjAfl0oFcBbekGeAXwG4AUKEiwESVFWTd2IYOSjTcBIvFIVqfIYLAB7SqXi76AK3gBefIVwAiKGrJUMNuvVugAZlXLSIAT3hKXVR7Nk5uGz5BKRk5BWkLI1MhdBgsVEsYmFl5RRBtIA

so there’s nothing new here, just two things working as expected that combine to look very strange in this case.

@andrewbranch andrewbranch added the Working as Intended The behavior described is the intended behavior; this is not a bug label Nov 8, 2021
@denis-sokolov
Copy link
Author

Thank you for your investigation, @andrewbranch! That sounds like a very precise explanation of what’s happening. This helps me find this to be a duplicate of #46449.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Working as Intended The behavior described is the intended behavior; this is not a bug
Projects
None yet
Development

No branches or pull requests

3 participants