2

I'm running into a peculiar typescript error when using the > operator with an optional chaining expression.

Example 1 : if(a?.length === 3)

Example 2 : if(a?.length > 0)

In both of the above examples, let's say a is inferred as Array<string> | undefined.

Problem: example 1 is totally fine, but example 2 throws a typescript error TS2532: Object is possibly 'undefined'. The error seems to clear off if I update example 2 to if(a && a.length > 0).

I understand it is something to do with strictnullchecks in TS, but no clue why example 1 works fine and doesn't throw an error. Is it something basic to do with how === and > operators evaluate? In the browser console, undefined > 0 and undefined === 0 evaluates as false, so I would assume there shouldn't be a problem in the if statement too in example 2, which is not the case.

I would appreciate it if someone could enlighten me on why example 2 throws a TS error.

novice
  • 446
  • 2
  • 5
  • 18
  • 2
    It throws an error because comparing `undefined` with a number is rarely intentional even though its behavior is well-defined (see https://stackoverflow.com/q/41750390/2887218 ). There is a relevant feature request at [ms/TS#45543](https://github.com/microsoft/TypeScript/issues/45543) which discusses this. Does that fully address your question? If so I'll write up an answer explaining; if not, what am I missing? – jcalz Feb 14 '23 at 22:04
  • I'm not expert with TS and tbh I'm quite surprised Example 1 compiles! – Robin Zigmond Feb 14 '23 at 22:13
  • 2
    FWIW, you can just use the [non-null assertion operator (postfix `!`)](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#non-null-assertion-operator-postfix-) instead: `if (a?.length! > 0) {...}` https://tsplay.dev/WkDk0w – jsejcksn Feb 14 '23 at 22:23
  • @jcalz Thanks for the reference That was super helpful. I understand now why the 2nd example was throwing an error. – novice Feb 14 '23 at 23:12

2 Answers2

1

As described in What does "all legal JavaScript is legal TypeScript" mean?, TypeScript may issue type warnings on code that it considers incorrect. JavaScript is very permissive compared to many other languages, and will happily allow you to do things like use the greater-than > operator to compare two completely unrelated values like undefined and 0. The JavaScript specification interprets undefined > 0 as if it were NaN > 0 (using the "not-a-number" NaN value), and NaN compares as false to everything else, no matter what. So undefined > 0 is well-defined in JavaScript. But is it good JavaScript?

That's subjective, but the whole point of TypeScript is that it attempts to overlay a static type system on top of JavaScript. If JavaScript had a stricter type system, it would definitely support x > y where x and y are both numbers, and it also would support it where x and y are both strings. But anything else is probably a mistake. Or at least that's the stance TypeScript takes.

TypeScript will be happy if you refactor that comparison so that you only use > on two numbers, such as by checking whether a is truthy first:

if (a && a.length > 0) { }

In the particular case you bring up, where optional chaining means you are possibly comparing undefined to a number, there is an open feature request to support it in microsoft/TypeScript#45543. It's marked as Awaiting More Feedback, which means the TS team would want to see more demand for it and the reasoning behind it before thinking of implementing it. If you really want to see it happen, you might want to give the issue a , but pragmatically speaking it is unlikely to make much difference.

In that issue it is mentioned that if you want this behavior right now you could use the non-nullish assertion operator ! to pretend that the possibly-undefined value is not undefined, and this will suppress the error:

if (a?.length! > 0) {}

So if you really need to use this technique instead of the type safe version, you can do so without waiting for an unlikely change to the language.

Playground link to code

jcalz
  • 264,269
  • 27
  • 359
  • 360
0

For the first example, the === is checking for both value and type, so basically you are checking undefined which is the same type of a after the optional chaining ? so the compiler infers that a is actually undefined, meanwhile > can't be used with an undefined value, so TS gives you that error.

you can prevent this issue by eg. adding a type guard if (a)... or if(a!.length > 0).

Hope that help.

Hamza Hsn
  • 74
  • 4
  • I think it's worth pointing out that using `if(a!.length > 0)` will throw a `TypeError` at runtime if `a` is actually `undefined` (because `undefined` has no `length` property) – jsejcksn Feb 15 '23 at 01:50