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

Discriminated union containing optional properties and generic types is not narrowed #55036

Open
TomerAberbach opened this issue Jul 16, 2023 · 4 comments
Labels
Possible Improvement The current behavior isn't wrong, but it's possible to see that it might be better in some cases
Milestone

Comments

@TomerAberbach
Copy link

Bug Report

I would have expected doesNotNarrow (below) to also be able to narrow x.b to NonUndefined<B>.

🔎 Search Terms

generic, narrow, discriminated, union, optional

🕗 Version & Regression Information

This is the behavior in every version I tried, and I reviewed all the FAQ for entries.

⏯ Playground Link

Playground link with relevant code

💻 Code

 type NonUndefined<T> = T extends undefined ? never : T

type X<A, B> = { a: NonUndefined<A>, b?: never } | { a?: never, b: NonUndefined<B> }

function doesNarrow(x: X<number, number>) {
  if (x.a === undefined) {
    const y = x.b
      // ^? number
  } 
}

function doesNotNarrow<A extends number, B extends number>(x: X<A, B>) {
  if (x.a === undefined) {
    const y = x.b
      // ^? NonUndefined<B> | undefined
  } 
}

🙁 Actual behavior

x.b was not narrowed.

🙂 Expected behavior

I expected x.b to be narrowed because if x.a is undefined, then x must be the latter part of the discriminated union because x.a can only be undefined there. TypeScript seems to be able to work that out when the type parameters are constant/non-generic (see doesNarrow), but for some reason it's not able to work it out for the generic case (even though NonUndefined should make it feasible).

@RyanCavanaugh
Copy link
Member

TypeScript doesn't have negated types, so attempts to recreate them through constructs like NonUndefined are sort of doomed to fail.

@RyanCavanaugh RyanCavanaugh added the Duplicate An existing issue was already created label Jul 24, 2023
@typescript-bot
Copy link
Collaborator

This issue has been marked as "Duplicate" and has seen no recent activity. It has been automatically closed for house-keeping purposes.

@typescript-bot typescript-bot closed this as not planned Won't fix, can't repro, duplicate, stale Jul 27, 2023
@TomerAberbach
Copy link
Author

Even without NonUndefined and with the generics constrained to number it can't figure it out

These two functions probably should behave identically in terms of narrowing

@TomerAberbach
Copy link
Author

Even without NonUndefined and with the generics constrained to number it can't figure it out

These two functions probably should behave identically in terms of narrowing

@RyanCavanaugh wondering if you'd be able to take a second look at this issue? Specifically my comment above? I think the issue isn't really a duplicate of the negated types feature request given the above playground link. What do you think?

@andrewbranch andrewbranch reopened this Nov 27, 2023
@andrewbranch andrewbranch added Possible Improvement The current behavior isn't wrong, but it's possible to see that it might be better in some cases and removed Duplicate An existing issue was already created labels Nov 27, 2023
@andrewbranch andrewbranch added this to the Backlog milestone Nov 27, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Possible Improvement The current behavior isn't wrong, but it's possible to see that it might be better in some cases
Projects
None yet
Development

No branches or pull requests

4 participants