Sitemap
JavaScript in Plain English

New JavaScript and Web Development content every day. Follow to join our 3.5M+ monthly readers.

null >= 0 is true, but null == 0 is false. Wait, what?

6 min readJul 2, 2026

--

Press enter or click to view image in full size

The JavaScript quirk that made me question everything I knew about comparisons

A few weeks ago, I was reviewing a pull request. Nothing fancy — a simple validation check that looked something like this:

if (userInput >= 0) {
processPayment(userInput);
}

Seems harmless, right? If the input is a non-negative number, process it. Except userInput was coming from an optional form field, and when the field was empty, it was null.

And here’s the kicker: null >= 0 is true in JavaScript.

That empty field sailed right through the validation. I stared at my screen for a good minute before opening the console to confirm I wasn’t losing my mind:

null >= 0   // true
null == 0 // false

Both of these are correct. Both of these are real. And if that combination doesn’t bother you at least a little, you’re either a JavaScript veteran who has already made peace with it, or you haven’t thought about it hard enough yet.

Let’s break down why this happens — because once you understand the why, it actually stops being magic and starts being (weirdly) logical.

Two operators, two completely different rulebooks

The root of the confusion is this: we assume == and >= are cousins. They look related. They feel related. Surely >= is just "greater than, OR equal to" — so if null >= 0 is true, and null > 0 is false, then null == 0 should be true, right?

Wrong. Because in JavaScript, equality operators and relational operators follow entirely different specifications. They don’t share logic. They barely share a philosophy.

Let me show you what each one actually does under the hood.

How null >= 0 becomes true

Relational operators (<, >, <=, >=) have one job: compare two values numerically (or lexicographically for strings). To do that, JavaScript runs both operands through an abstract operation the spec calls ToNumber().

And what does null convert to as a number?

Number(null)   // 0

Zero. null becomes 0.

So the engine quietly rewrites your comparison:

null >= 0
// becomes
0 >= 0
// which is obviously
true

That’s it. No magic. null was coerced into 0, and zero is indeed greater than or equal to zero.

The same logic explains this whole family of results:

null > 0    // false  →  0 > 0
null >= 0 // true → 0 >= 0
null < 0 // false → 0 < 0
null <= 0 // true → 0 <= 0

Perfectly consistent — within its own rulebook.

So why isn’t null == 0 also true?

Here’s where most people’s mental model breaks. If null converts to 0 in comparisons, surely loose equality — famous for its aggressive type coercion — would do the same thing?

Nope. The == operator doesn't use ToNumber() on null at all. Instead, the spec carves out a special case just for null and undefined:

null is loosely equal to undefined, and to itself. Nothing else.

That’s the entire rule. No coercion, no conversion, no exceptions.

null == undefined   // true
null == null // true
null == 0           // false
null == false // false
null == '' // false
null == NaN // false

Notice how strange that middle section is. 0, false, and '' are all falsy values that happily coerce into each other:

0 == false   // true
0 == '' // true
'' == false // true

But null? It refuses to join the party. It sits in its own corner with undefined, and the coercion rules that apply to every other value simply don't apply to it.

Try it yourself

Don’t take my word for it. Open your browser console right now (F12 → Console) and paste this in:

console.log('null == 0  →', null == 0);    // false
console.log('null >= 0 →', null >= 0); // true
console.log('null > 0 →', null > 0); // false
console.log('null <= 0 →', null <= 0); // true
console.log('null < 0 →', null < 0); // false

Read those results out loud. “Null is not equal to zero. Null is greater than or equal to zero. Null is not greater than zero.”

Get Piyush Mishra’s stories in your inbox

Join Medium for free to get updates from this writer.

If a coworker said that sentence to you in a meeting, you’d assume they were having a stroke. In JavaScript, it’s just Tuesday.

The mental model that finally made it click for me

Stop thinking of >= as "greater than OR equal to." That framing is the trap.

Instead, think of it like this:

  • Relational operators (>, >=, <, <=) say: "Convert everything to numbers, then compare."
  • Loose equality (==) says: "I have my own hand-written table of what equals what, and null only matches undefined."
  • Strict equality (===) says: "Different types? Not equal. Done."

Three operators, three philosophies. They were never designed as a coherent set — they were designed at different times to solve different problems, and null fell through the cracks between them.

Fun bit of history: undefined, the value null is exclusively equal to, doesn't even follow null's numeric behavior:

Number(null)        // 0
Number(undefined) // NaN
undefined >= 0      // false (NaN >= 0 is false)
null >= 0 // true

So the two values that are “equal” to each other behave completely differently in every other comparison. Beautiful, isn’t it?

Why this matters in real code

Back to that pull request. The bug pattern looks like this:

// ❌ Dangerous: null slips through
function isValidAmount(amount) {
return amount >= 0;
}
isValidAmount(null);   // true — uh oh
isValidAmount(0); // true
isValidAmount(-5); // false

An optional field, a missing API response, an uninitialized variable — any of them can hand you a null, and your "must be non-negative" check will wave it through with a smile.

The fix is to be explicit about what you’re actually checking:

// ✅ Safe: verify it's actually a number first
function isValidAmount(amount) {
return typeof amount === 'number' && amount >= 0;
}
isValidAmount(null);   // false
isValidAmount(0); // true
isValidAmount(-5); // false

Or, if you’re in a TypeScript codebase, let the type system reject null before it ever reaches the comparison. This entire class of bug is one of the quieter arguments for TypeScript adoption that nobody puts on the marketing slides.

The takeaways

If you remember nothing else from this article, remember these four lines:

  1. >= converts operands to numbers. null becomes 0, so null >= 0 is true.
  2. == has a special rule for null. It equals only undefined and itself — never 0, never false, never ''.
  3. These two behaviors will never agree, because they come from different parts of the spec with different goals.
  4. Never rely on comparisons to filter out null. Check for it explicitly, or use strict typing.

JavaScript isn’t broken here — it’s doing exactly what the ECMAScript specification says, line by line. The problem is that the specification encodes two incompatible ideas about what null "means," and it's on us to know which idea is in play for each operator.

Or, as I now say in every code review where I spot a bare >= 0 check:

“Cool, but what happens when it’s null?”

The answer is almost never what the author expected.

Have you been bitten by this one — or by another JavaScript coercion gotcha? I’d genuinely love to hear the war stories. Drop them in the comments.

If this saved you a future debugging session, a clap or two helps this reach the next developer about to write if (value >= 0) on a nullable field. 👏

Tags: JavaScript, Web Development, Programming, Coding, Software Engineering

Thousands of developers share what they’re building, learning, and discovering across our publications every month. One account connects you to our entire network of publications and communities. Explore more at plainenglish.io.

--

Piyush Mishra
Piyush Mishra

Written by Piyush Mishra

Software engineer exploring frontend engineering, problem-solving, and the journey to becoming a better developer.

JavaScript in Plain English
JavaScript in Plain English

Published in JavaScript in Plain English

New JavaScript and Web Development content every day. Follow to join our 3.5M+ monthly readers.