Skip to content

someBool || true gets the literal type true when assigned to a let variable at initialization (unlike true getting the type boolean) #61799

Closed as not planned

Description

@aweebit

🔎 Search Terms

boolean literal type true false assign expression

🕗 Version & Regression Information

⏯ Playground Link

https://www.typescriptlang.org/play/?#code/CYUwxgNghgTiAEEQBd4GcD2BbEAhDGEAXPAEYFJQB2A3AFB1KogAeUWADkgIzwC88ZDACuIek3it2XEACZ+6bHgrwAPqsEixQA

💻 Code

declare let someBool: boolean;

let okay = true;
let weird = someBool || true;

🙁 Actual behavior

weird is assigned the type true, whereas okay is of type boolean.

🙂 Expected behavior

Both variables have the type boolean.

Additional information about the issue

Stumbled upon this while writing react/react#33399.

Very unexpected and confusing behavior, I highly doubt it is intended. Couldn't find anything explaining the change in the release notes for TypeScript 4.2, so pretty sure it's an unwanted regression. Please correct me if I'm wrong.

Activity

  1. changed the title [-]The expression `someBool || true` is of type `true` while the expression `true` is of type `boolean`[/-] [+]`someBool || true` gets the literal type `true` when assigned to a re-assignable variable (unlike `true` getting the type `boolean`)[/+] on Jun 1, 2025
  2. changed the title [-]`someBool || true` gets the literal type `true` when assigned to a re-assignable variable (unlike `true` getting the type `boolean`)[/-] [+]`someBool || true` gets the literal type `true` when assigned to a `let` variable at initialization (unlike `true` getting the type `boolean`)[/+] on Jun 1, 2025
  3. Andarist commented on Jun 2, 2025

    @Andarist
    Contributor

    I'm pretty sure this has changed in #42353 . checkBinaryExpressionWorker calls getUnionType here on the left and right type and after the change, it eliminates the fresh true type in favor of its regular type.

  4. RyanCavanaugh commented on Jun 3, 2025

    @RyanCavanaugh
    Member

    I don't really know what's confusing or unexpected about a value that we know has the value true having the type true. Absent a more principled reason to change this, let's not break something else in the process.

  5. snarbles2 commented on Jun 3, 2025

    @snarbles2

    I don't necessarily think it warrants a change, but I think it's fair to characterize the inconsistent behavior as confusing or unexpected.

  6. aweebit commented on Jun 3, 2025

    @aweebit
    Author

    I don't really know what's confusing or unexpected about a value that we know has the value true having the type true.

    The confusing part is the fact that it's different from how an even simpler true expression results in a wider type in the end.

    With let x = true it couldn't be more clear that the value will be true, yet the variable ends up having the boolean type assigned to it rather than true. That, of course, happens for a good reason. Why should someBool || true behave any different then?

  7. Andarist commented on Jun 3, 2025

    @Andarist
    Contributor

    By the applied reasoning, this should be string | true but it's not:

    declare const str: string
    let example3 = str || true
    //   ^? let example3: string | boolean

    We could say though that the right operand is contextually-typed by the left one:

    declare const maybeFn: ((arg: number) => void) | null;
    let example4 = maybeFn || ((arg) => {});
    //   ^? let example4: (arg: number) => void

    But that principle doesn't fully apply to primitives (albeit maybe for some other reasons, widening behavior of strings/numbers was always confusing to me):

    declare const maybeString: "" | null
    let example5 = maybeString || ""
    //   ^? let example5: string

    It's also worth noting boolean has some history of being special-cased so it doesn't always get typed contextually, for example in #48380

  8. typescript-bot commented on Jun 6, 2025

    @typescript-bot
    Contributor

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

  9. locked as resolved and limited conversation to collaborators on Oct 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Not a DefectThis behavior is one of several equally-correct options

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions