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

Nullish coalescing and conditional type narrowing #48536

Closed
vlovich opened this issue Apr 3, 2022 · 8 comments
Closed

Nullish coalescing and conditional type narrowing #48536

vlovich opened this issue Apr 3, 2022 · 8 comments
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed

Comments

@vlovich
Copy link

vlovich commented Apr 3, 2022

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

@fatcerberus
Copy link

fatcerberus commented Apr 3, 2022

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.

@RyanCavanaugh RyanCavanaugh added the Design Limitation Constraints of the existing architecture prevent this from being fixed label Apr 4, 2022
@RyanCavanaugh
Copy link
Member

What @fatcerberus said ⬆️

@fiddlerwoaroof
Copy link

fiddlerwoaroof commented Apr 8, 2022

@fatcerberus does that also apply to something like:

const a: string | null | undefined = ...;

if (a ?? '' === '') {
  return;
}

const c: string = a;

@fatcerberus
Copy link

fatcerberus commented Apr 8, 2022

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

@cheruvian
Copy link

cheruvian commented Aug 16, 2023

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.

@cheruvian
Copy link

Was this closed as completed because it has been implemented?

@RyanCavanaugh
Copy link
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".

@RyanCavanaugh RyanCavanaugh closed this as not planned Won't fix, can't repro, duplicate, stale Mar 4, 2024
@cheruvian
Copy link

sadge. Thanks

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed
Projects
None yet
Development

No branches or pull requests

5 participants