#Type narrowing of property when done via variable?

20 messages · Page 1 of 1 (latest)

upper brook
#

This is something that just blocked me for a hot second in a build pipeline of ours - apparently when I outsource a boolean check on if a value is there or not into a variable it doesn't quite work?
Though only for properties apparently.

So this does cause TS errors:

function x(val: { current: undefined | number}): number {
  const hasValue = val.current !== undefined
  if(hasValue){
    return val.current
  }
  return 5
}

While this does not:

function x(val: undefined | number): number {
  const hasValue = val !== undefined
  if(hasValue){
    return val
  }
  return 5
}

I'm trying to look more into this but I'm not sure what this is called so what to google for.
Is this a bug?
Is this a missing feature that is super complex to implement?
Is this intentional and I don't see why/how?

tidal hatch
#

In first example when you directly check val.current !== undefined in if condition, it will narrow down the type correctly, similar example is used also in official docs on narrowing.

#

It is indeed visible though that compiler is treating simple values and objects differently inside functions. Could be related to mutability of objects or how easy is to track those indirect type relations for compiler

upper brook
#

Main reason I noticed is that in scenarios where a value being absent has a deeper meaning I like to outsource it into a boolean variable with an explanatory name

tidal hatch
#

yes I like that pattern myself hence i was curious. Hopefully someone can shine more light on the internal behavior

upper brook
#

So chances are, val.current is not immutable on a type level since its a ref.
Therefore, all bets are off what happens between the variable assignment and the if-statement within which it is used

tidal hatch
#

Oh yeah now it makes sense

#

we've taken for granted way too much 🙂

quick linden
#

Move the check inside the if statement?

upper brook
# quick linden Move the check inside the if statement?

Aye, that works, just wans't understanding as to why.
I am however wondering if allowing the type narrowing at all wasn't a mistake potentially.
Because this does not behave as I would have expected:

const evilGlob: { current: number | undefined } = { current: 5 }

function evilMutation(){
  evilGlob.current = undefined
}

function x(val: { current : number | undefined }): number {
  if (val.current !== undefined){
    evilMutation()
    return val.current
  }

  return 5;
}

console.log(x(evilGlob))

this will happily compile and run and spit out undefined when the type-signature guarantees me it doesn't

quick linden
#

Don't be stupid then I guess 😅

upper brook
#

But I am! This is why I use TS and not JS

#

Shared refs, the root of all evil

quick linden
#

I don't really know your situation, but most of the time where I personally rely on this narrowing, it looks like this:

if (stuff.maybeUndefined === undefined) stuff.maybeUndefined = 5;
if (response.status !== 200) return 5;
```Mostly inline stuff that handles this case. Once you do more, the code already gets a bit messy IMO.
#

Also this:

const value = stuff.maybeUndefined;
if (value === undefined) throw new Error("Oh no!");
#

Just saving the value is really handy

paper tendon