Skip to content

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

Closed
@MichaelMitchell-at

Description

@MichaelMitchell-at

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.

Metadata

Metadata

Assignees

Labels

BugA bug in TypeScriptFix AvailableA PR has been opened for this issue

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions