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

Narrowing with typeof and optional chaining does not work specifically with a union containing boolean unless wrapped in parentheses #51700

Closed
MichaelMitchell-at opened this issue Nov 30, 2022 · 3 comments Β· Fixed by #51720
Assignees
Labels
Bug A bug in TypeScript Fix Available A PR has been opened for this issue

Comments

@MichaelMitchell-at
Copy link

MichaelMitchell-at commented Nov 30, 2022

Bug Report

πŸ”Ž Search Terms

typeof boolean narrow optional chaining

πŸ•— Version & Regression Information

  • This changed between versions 4.8.4 and 4.9.3

⏯ Playground Link

Playground link with relevant code

πŸ’» Code

type WrappedStringOr<T> = {value?: string} | {value?: T};

function numberOk(wrapped: WrappedStringOr<number> | null) {
  if (typeof wrapped?.value !== 'string') {
    return null;
  }
  return wrapped.value;
}

function booleanBad(wrapped: WrappedStringOr<boolean> | null) {
  if (typeof wrapped?.value !== 'string') {
    return null;
  }
  // @ts-expect-error Unexpected type error here
  return wrapped.value;
}

function booleanFixed(wrapped: WrappedStringOr<boolean> | null) {
  // This is semantically equivalent to above format (and gets auto-fixed to the above by formatters like Prettier).
  if (typeof (wrapped?.value) !== 'string') {
    return null;
  }
  // No type error here
  return wrapped.value;
}

πŸ™ Actual behavior

The type of wrapped is narrowed to exclude the {value?: boolean} case, but doesn't exclude the null case.

πŸ™‚ Expected behavior

The type of of wrapped is narrowed to both exclude the {value?: boolean} case and also exclude the null case.

@MichaelMitchell-at MichaelMitchell-at changed the title Narrowing with typeof and optional chaining does not work specifically with a union containing boolean unless wrapped in paretheses Narrowing with typeof and optional chaining does not work specifically with a union containing boolean unless wrapped in parentheses Nov 30, 2022
@ahejlsberg
Copy link
Member

@typescript-bot bisect good v4.8.4 bad v4.9.3

@ahejlsberg
Copy link
Member

Looks like this was broken by #50344.

@ahejlsberg ahejlsberg self-assigned this Dec 1, 2022
@ahejlsberg ahejlsberg added the Bug A bug in TypeScript label Dec 1, 2022
@ahejlsberg ahejlsberg added this to the TypeScript 5.0.0 milestone Dec 1, 2022
@ahejlsberg
Copy link
Member

Shorter repro:

function foo(obj: { kind: 'a', data: string } | { kind: 1, data: number } | undefined) {
    if (typeof obj?.kind === "string") {
        obj;  // { kind: 'a', data: string } | undefined, but shouldn't include undefined
    }
    else {
        obj;  // { kind: 1, data: number } | undefined
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Fix Available A PR has been opened for this issue
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants