Skip to content

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

Closed
@karol-majewski

Description

@karol-majewski

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

Activity

added
Working as IntendedThe behavior described is the intended behavior; this is not a bug
and removed
Working as IntendedThe behavior described is the intended behavior; this is not a bug
on Feb 13, 2021
changed the title [-]Unions containing `void` behave differently from 4.2 on[/-] [+]Inference to unions containing `void` behave differently from 4.2 on[/+] on Feb 13, 2021
DanielRosenwasser

DanielRosenwasser commented on Feb 13, 2021

@DanielRosenwasser
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

ahejlsberg commented on Feb 15, 2021

@ahejlsberg
Member

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

DanielRosenwasser commented on Feb 17, 2021

@DanielRosenwasser
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

ahejlsberg commented on Feb 17, 2021

@ahejlsberg
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.

self-assigned this
on Feb 17, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

    Development

    Participants

    @DanielRosenwasser@ahejlsberg@karol-majewski

    Issue actions

      Inference to unions containing `void` behave differently from 4.2 on · Issue #42786 · microsoft/TypeScript