Skip to content

Nullish coalescing and conditional type narrowingΒ #48536

Closed as not planned
Closed as not planned

Description

@vlovich

Suggestion

Conditional + nullish coalescing doesn't seem to reify the type correctly.

πŸ” Search Terms

nullish coalescing
nullish coalescing conditional

#43705 sounds maybe related but hard to tell (examples listed don't seem the same)

βœ… Viability Checklist

  • [Y] This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • [Y] This wouldn't change the runtime behavior of existing JavaScript code
  • [Y] This could be implemented without emitting different JS based on the types of the expressions
  • [Y] This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
  • [Y] This feature would agree with the rest of TypeScript's Design Goals.

⭐ Suggestion

Conditional type narrowing through the || operator + the use of the nullish coalescing operator should understand each other to reify the type correctly.

πŸ“ƒ Motivating Example

declare function foo(): string | undefined

const a = foo()
const b = foo()
if (a != undefined || b != undefined) {
  const c = a ?? b
}

c should have type string but instead it has type string | undefined because TS hasn't reasoned that the only way into the block is if a ?? b must be non-null.

πŸ’» Use Cases

Came up in my code where I had two strings and each could be string | undefined and I wanted to nullish coalesce but only if one of them was not undefined. Had to use ! as a workaround

Activity

  1. fatcerberus commented on Apr 3, 2022

    @fatcerberus

    Type narrowing is done only at the level of individual variables, not entire expressions. As such, combining type guards via disjunction || generally won't narrow anything; TS has no way to track "at least one of a or b is non-null but I don't know which". See for example #31603.

  2. RyanCavanaugh commented on Apr 4, 2022

    @RyanCavanaugh
    Member

    What Bruce Pascoe (@fatcerberus) said ⬆️

  3. fiddlerwoaroof commented on Apr 8, 2022

    @fiddlerwoaroof

    Bruce Pascoe (@fatcerberus) does that also apply to something like:

    const a: string | null | undefined = ...;
    
    if (a ?? '' === '') {
      return;
    }
    
    const c: string = a;
  4. fatcerberus commented on Apr 8, 2022

    @fatcerberus

    Ed Langley (@fiddlerwoaroof) No, that's not the same thing. a ?? '' === '' doesn't even count as a type guard. TS only recognizes a handful of common patterns as type guards, more complex checks like that won't produce any narrowing at all. This issue is about expressions which do count as type guards, e.g. x != undefined or typeof x === 'string', that when these are combined with || it creates uncertainty about which case applies that prevents the compiler from doing any narrowing.

  5. cheruvian commented on Aug 16, 2023

    @cheruvian

    Ran into this today as well, in a way that Typescript certainly seems like it should handle.

    declare function foo(): string | undefined
    
    const a = foo()
    const b = foo()
    if (a ?? b) {
      const c = a ?? b
      // or the actual place I've been seeing it
      console.log(`${a ?? b}`) // eslint warning for non-string in template literal
    

    Bummer this can't be handled yet but figured I'd add +1 / another use case here.

  6. cheruvian commented on Mar 3, 2024

    @cheruvian

    Was this closed as completed because it has been implemented?

  7. RyanCavanaugh commented on Mar 4, 2024

    @RyanCavanaugh
    Member

    The bulk "Close" action I applied to Design Limitation issues doesn't let you pick what kind of "Close" you're doing, so don't read too much into any issue's particular bit on that. I wish GitHub didn't make this distinction without a way to specify it in many UI actions!

    The correct state for Design Limitation issues is closed since we don't have any plausible way of fixing them, and "Open" issues are for representing "work left to be done".

  8. cheruvian commented on Mar 4, 2024

    @cheruvian

    sadge. Thanks

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

    Design LimitationConstraints of the existing architecture prevent this from being fixed

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions