-
Notifications
You must be signed in to change notification settings - Fork 12.7k
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
Allow ... | undefined
in comparisons (<
, >
, etc)
#61141
Comments
undefined
(_not_ null
) in comparisons (<
, >
, etc)undefined
(but _not_ null
) in comparisons (<
, >
, etc)
undefined
(but _not_ null
) in comparisons (<
, >
, etc)undefined
(not null
) in comparisons (<
, >
, etc)
undefined
(not null
) in comparisons (<
, >
, etc)number | undefined
(not number | null
) in comparisons (<
, >
, etc)
number | undefined
(not number | null
) in comparisons (<
, >
, etc)number | undefined
in comparisons (<
, >
, etc)
number | undefined
in comparisons (<
, >
, etc)... | undefined
in comparisons (<
, >
, etc)
Duplicate of #45543.
You can just use the non-null assertion operator: declare const value: number | undefined;
if (value! > 123) {} |
I really question how good an idea this is, even in principle. Are you not worried about someone refactoring this code if (student.score > 95) {
console.log("Good job!")
} into this
without realizing they've changed the behavior? |
Not particularly, as the type would still specify that it could be Future developers refactoring without paying attention to types is always a possibility, and quite unavoidable since people will do what people do. The question is, does a particular limitation help more than it hinders (or makes inconvenient). In the case of But in this case, with Logically speaking, is |
People have made exactly this case in favor of allowing implicit coercion from "No implicit coercions, except for the ones I personally like" is just a very difficult line to draw |
Definitely not a duplicate—that one includes |
I can see the potential tangential relationship. However, I’m trying to keep this issue more focused specifically on comparisons ( When comparing this to something like the bitwise operators one, the question becomes, how should undefined behave in a bitwise operation? It’s not expressly clear, thinking about the question mathematically or logically. What should I wouldn’t be a proponent of allowing mixed types here, just allowing types to be compared to the undefined-able version of themselves. We already allow implicit coercion to number when doing comparisons between the same types ( The only proposed change is to allow types to be compared to themselves or I’d be fine with only one side being optional, but that would be extra complication that I don’t want to be married to here. |
For example, we allow optional types in equality checks. That presents the same issue, but we still have type checking to show that score is optional. if (student.score === 100) {
console.log("Wow, perfect score!")
} Versus if (student.score !== 100) {
console.log("You didn’t get a perfect score.")
} else {
console.log("Wow, perfect score!")
} |
Regarding using the non-null assertion—ignoring the fact that this would be an incorrect assertion (since the value could very well be undefined), we have lint rules in place in our company that disallows non-null assertions since they can and do lead to hard to debug errors. While I appreciate the response, work-arounds like "you can just use non-null assertions," "you can just use a |
I created this issue to isolate the The point here is that since the If nothing else, I’m saying that using an |
I don't really understand the argument that If it may be undefined, and you want to treat it as a number, that's exactly specifically what the Edit: re:
IIRC there is an open issue requesting this be changed. It's allowed in order to permit defensive programming, but it can and does cause problems, and there is the view (which I agree with) that checking for invalid data should be done at the boundaries of your environment instead of hoping you catch it downstream. |
I've seen several projects migrate from JavaScript to TypeScript, and JS code is very often not strict with using What's So it's very helpful for TypeScript to say "hey, this value may be absent - you should handle that", regardless of whether that absence is indicated with I think the FAQ entry "The ECMAScript Spec is Descriptive, not Normative" really applies here: https://github.com/microsoft/TypeScript/wiki/FAQ#the-ecmascript-spec-is-descriptive-not-normative Just because the implicit coercions are defined, doesn't mean I want them. |
By
The point is that it’s not an invalid comparison. It’s a perfectly valid comparison. Let’s say you are writing a paper that you wanted to publish in 2024. If you were to be asked, "Hey, that paper you were writing, did you get it published before the start of 2025?" (essentially In practice, this becomes very useful when used in contexts where you have known optional values, maybe when you don’t control the source (it comes from an API, etc), like
No logic error necessary, as we covered above. And that's what lint rules are for. If you personally want to be more restrictive in your comparisons, use a lint rule that checks that in your projects. Why should it throw an error because you would like it to fail there in your project? What you may consider an "invalid" comparison, others may not consider invalid (with good reason).
It’s called the What I’m arguing is that the language should not take a dogmatic stance on something that isn’t so cut and dry. Leave it either to the linters, or (despite the undesirability of new options) an option. It behaves the way you’d expect, evaluating to
There’s also All of that said, I can see the value of restricting it—for users to be warned, or even want a failure in these cases, if the compiler allows it, they would feel like the compiler is too loose. I tend to take the position that the compiler should be more permissive, and if users want more strict rules, they can use lint rules (or stricter compiler options) on their projects. For this specific feature, the only recourse to use when the workarounds are either not satisfactory or not allowed by company, team, or personal restrictions, is completely disabling |
For my specific use case, I’m having to use a third party GraphQL API where they have a lot of deeply nested fields, under a lot of optional layers, and it becomes a huge hassle. For each |
I appreciate this sentiment. My point here is that the behavior of That said, the fact that much JS code isn’t strict about |
I perceive this is an uphill battle, so I’ll say this—this isn’t a hill I’m willing to die on—it’s just an annoyance that I figured I’d open an issue for. The issues that are already open that are related to this seem to group together |
Just tossing out there for the discussion, technically I guess there's a few other recourses... Nullish coalesingif ((a.long?.optional.chaining?.expression ?? Infinity) <= 24) { } Kinda yucky since you have to reason about the default value, but removes the ambiguity about both nullish cases, including possible IIIFETightly scoping a single-use intermediate variable via an IIFE const someObject = {
resultOfComparison: (() => {
const theThing = a.long?.optional.chaining?.expression;
return theThing != null && theThing <= 24;
})()
// cannot access intermediate variable afterwards.
} Neither of these is as concise, to be sure, but neither depending on deep knowledge of coercion. Suppress TSI would agree with the point that using the non-null assertion operator is quite undesirable. I'd just hit the code with a TS error suppression if i really wanted to write this code // @ts-expect-error -- I know what happens when `undefined` is used in a comparison.
if (maybe.undefined.length >= 2) {
} but of course that is an extremely blunt instrument. The other thing to think about is how understandable is a case where both sides of the comparison might be undefined? Seems like a rather unpleasant situation to reason about. if (maybeUndefined < alsoMaybeUndefined) {} |
I actually do like this solution more than the others. Still not as concise as just allowing it to happen implicitly, but it’s OK. For further explicitness, I’ll probably start coalescing to if ((a.long?.optional.chaining?.expression ?? NaN) <= 24) { } |
"Did you get it published before the start of 2025?" is actually two questions: "Did you get it published?" and "Is the publication date before the start of 2025?". So But this example is a bit confusing because you can’t retroactively publish a paper. If instead you already published the paper and you were to be asked "Did you manage to publish the paper before the deadline?", would you answer "No" if there was actually no deadline? If there is no deadline, surely that means every paper was published "before the deadline", right?
I would personally find it much more explicit to default to |
🔍 Search Terms
comparison, strictNullChecks, undefined, greater than, less than, nullable, optional
✅ Viability Checklist
⭐ Suggestion
Please allow values that are undefined-able, like
number | undefined
, to be used in direct comparisons, like<
,>
, etc.I know the more general request has been made for allowing "nullable" numbers (
numbers | null
and/ornumber | undefined
) to be used in comparisons, see #17801, but, in my opinion, that is too broad. This request is specifically for allowingnumber | undefined
in comparisons, notnumber | null
. This would make comparisons of values that are optional, but not nullable, much simpler.📃 Motivating Example
In JavaScript,
undefined
coerces intoNaN
when coerced into a number, like when used in a comparison (contrast that withnull
, which coerces to0
and would break code—again null is not included here).Since
undefined
coerces toNaN
, this would evaluate tofalse
in all comparisons (even with anotherundefined
value), which would match the expected behavior (in all except maybeundefined <= undefined
andundefined >= undefined
, which evaluate tofalse
). This wouldn’t be a breaking change because any code that already compiles would still compile. No functionality would be different.Given the one minor caveat of greater-than-or-equal and less-than-or-equal, which I estimate to be a minor caveat which would only come into play if comparing two optional numbers and expecting to take them as equal if they are both
undefined
, I think this feature would help to simplify code a lot.Obviously that is more concise than the required, and redundant null check.
This is even more pronounced when this is a calculated value from a long running function, maybe in a ternary context where you can’t easily save it to a variable ahead of time. You end up having to use a IIFE:
In the above trivial example, if the heavy function only gets evaluated conditionally, it’s not so simple to save it to a variable to perform the undefined check (obviously in this simple example, you could evaluate the initial ternary to a variable, but it’s just an example).
💻 Use Cases
This would greatly simplify comparisons of optional numbers.
You can’t always save it to a variable to avoid evaluating functions twice, and it’s not so simple to check undefined-ness inline sometimes.
Having to do some ugly constructs to conditionally evaluate heavy functions and save to variables, then use that optional variable instead of simply evaluating the function in the comparison. It’s way more verbose than just allowing
undefined
in comparisons.The text was updated successfully, but these errors were encountered: