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

Inference to unions containing void behave differently from 4.2 on #42786

Closed
karol-majewski opened this issue Feb 13, 2021 · 4 comments · Fixed by #42846
Closed

Inference to unions containing void behave differently from 4.2 on #42786

karol-majewski opened this issue Feb 13, 2021 · 4 comments · Fixed by #42846
Assignees

Comments

@karol-majewski
Copy link

karol-majewski commented Feb 13, 2021

Bug Report

🔎 Search Terms

void, union, user-defined type guard

🕗 Version & Regression Information

v4.2.0-dev.20210207. It stayed this way in 4.3.0-dev.20210211

  • This changed between versions 4.1 and 4.2 RC
  • This is the behavior in every version I tried, and I reviewed the FAQ for entries about void

⏯ Playground Link

Playground link with relevant code

💻 Code

function isDefined<T>(value: T | undefined | null | void): value is T {
  return value !== undefined && value !== null;
}

declare const foo: string | undefined;

if (isDefined(foo)) {
  // 4.2: foo is string | undefined
  // 4.1: foo is string
  console.log(foo.toUpperCase()); 
}

🙁 Actual behavior

Compile-time error. foo is string | undefined

🙂 Expected behavior

No compile-time error. foo is string

@DanielRosenwasser DanielRosenwasser added Working as Intended The behavior described is the intended behavior; this is not a bug and removed Working as Intended The behavior described is the intended behavior; this is not a bug labels Feb 13, 2021
@DanielRosenwasser DanielRosenwasser changed the title Unions containing void behave differently from 4.2 on Inference to unions containing void behave differently from 4.2 on Feb 13, 2021
@DanielRosenwasser
Copy link
Member

Likely involves the changes from #42353, and I have theories of what we probably do, but @ahejlsberg will surely have a better idea.

@ahejlsberg
Copy link
Member

ahejlsberg commented Feb 15, 2021

Yeah, this one is an effect of us now reducing void | undefined to just void. With that reduction, the type of the value parameter becomes T | null | void, but then the value !== undefined test in the body doesn't remove void because we sometimes think of void possibly being any type. So, we're trying to have our cake and eat it too, and it isn't working.

We've had many discussions about our strange void semantics, but we have yet to come up with something better that doesn't break the world. However, in the spirit of at least not breaking things, we could dial back and only do the void | undefined to void reduction in cases where we perform subtype reduction. That was our previous behavior.

@DanielRosenwasser
Copy link
Member

@ahejlsberg is that an option? Is it just a matter of special-casing how we invoke literal type reduction? That might be worth it.

@ahejlsberg
Copy link
Member

is that an option? Is it just a matter of special-casing how we invoke literal type reduction?

Yes, it's an option and it isn't particularly hard to implement. We'd just make the void | undefined to void reduction in the literal type reduction pass conditional on whether we are also (in a subsequent pass) performing subtype reduction.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants