Most JavaScript style guides ban ==, yet many carve out an exception for the form x == null. What does that check actually match, and what would you require of a team that allows the exception?
answer
- exactly two values, no conversion involved
- zero and empty string survive it
- not the same as if (!x)
- the carve-out needs a machine to police it
basics
~20 sx == null is true for exactly two values, null and undefined, so it is a precise nullish check that preserves 0, empty strings and false. Allow it only as that fixed idiom, enforced by tooling, so any other == still stands out as a defect.
solid answer
~50 s`x == null` matches `null` and `undefined` and nothing else, because the loose-equality algorithm pairs those two values explicitly and never converts either one. That makes it exactly equivalent to `x === null || x === undefined`, and importantly *not* a falsiness test: `0`, `''`, `NaN` and `false` all pass through as real values. The reason a team allows it is that this is the check people usually mean when they write `if (!x)` and get a data-loss bug. The conditions I would attach are: enforce the carve-out mechanically so that only this exact shape survives lint, since a reviewer cannot otherwise distinguish a deliberate `==` from a typo; require it only where `null` and `undefined` genuinely mean the same thing; and prefer `??` or a default parameter where the goal is supplying a fallback value rather than branching.
code
javascript · 9 linesfunction greet(name) {
if (name == null) name = 'guest'; // matches null and undefined only
return `hello, ${name}`;
}
console.log(greet()); // hello, guest
console.log(greet(null)); // hello, guest
console.log(greet('')); // hello, <- empty string preserved
console.log(greet(0)); // hello, 0 <- zero preservedgo deeper
Know that x == null is true only for null and undefined, and that it keeps 0 and the empty string as real values, unlike if (!x).
Explain why the idiom is exact — an explicit algorithm step with no conversion — and contrast it with a falsiness check, naming the zero-and-empty-string data-loss bug that motivates it.
Argue the operational conditions: the carve-out must be enforced by tooling so a stray == still fails the build, and it is only safe where null and undefined genuinely mean the same thing in that field.
Own the policy angle: an exception a human must police is not an exception, it is a hole. Push absence normalisation into the parsing boundary so most call sites never need the check at all.
## What the check is `x == null` is true for `null` and for `undefined`, and for no other value in the language. That comes straight out of the loose-equality algorithm, which contains an explicit step making `null` and `undefined` equal to each other, and contains no rule that converts either of them to another type. So the check is exactly equivalent to `x === null || x === undefined`, in one third of the characters, and its negation `x != null` reads as "x has a real value". ```js console.log(0 == null); // false console.log('' == null); // false console.log(false == null); // false console.log(NaN == null); // false console.log(null == null); // true console.log(undefined == null); // true ``` ## Why the exception exists at all The alternative that people reach for is `if (!x)`, and it is wrong in a specific, expensive way: it also fires for `0`, `''`, `NaN`, `-0`, `0n` and `false`. Every one of those is a legitimate value in ordinary domains — a quantity of zero, an intentionally cleared text field, a switched-off toggle, a coordinate at the origin. The classic production bug is a defaulting branch that silently replaces a real zero with a fallback, and it survives testing because the fixture data never contains zero. ```js function withDefault(v) { return v == null ? 'fallback' : v; } console.log(withDefault(0)); // 0 - preserved console.log(withDefault('')); // '' - preserved console.log(withDefault(null));// 'fallback' ``` So the carve-out is not a stylistic indulgence; it is the shortest correct spelling of a check that people routinely get wrong. ## The conditions I would attach **Enforce the shape mechanically.** The entire value of banning `==` is that any occurrence is a review signal. Allowing it "when you mean the nullish check" by convention destroys that: a reviewer seeing `a == b` cannot tell intent from typo. The carve-out must be encoded in the linter so that only comparisons against the literal `null` pass, and everything else still fails the build. If the tooling cannot express the narrow rule, ban `==` outright and write `x === null || x === undefined`; the verbosity is cheaper than an unreviewable exception. **Require that the two absent values are interchangeable here.** `undefined` conventionally means "never set, property absent, argument omitted", while `null` means "explicitly set to nothing". Some data sources rely on the distinction — a stored document where a missing key and a stored null mean different things, or an API that treats a null field as "clear this" and an absent field as "leave unchanged". Where the difference is load-bearing, the compact check erases information and you should test strictly for the one you mean. **Prefer the operators built for the job when the goal is a value, not a branch.** If the code is supplying a fallback, `x ?? fallback` expresses it directly, and a default parameter value covers the omitted-argument case. Reserve `x == null` for control flow — early returns, guard clauses, validation — where you are branching rather than substituting. **Do not let it drift into a general emptiness check.** `x == null` says nothing about empty arrays, empty objects, whitespace strings or `NaN`. A codebase that starts using it as "is this missing" will eventually apply it to `[]` and be surprised. Name the actual predicate: `arr.length === 0`, `Number.isNaN(n)`, `str.trim() === ''`. ## Reviewing an existing codebase When auditing, the two patterns worth grepping for are `!x` used as an absence test on values that could legitimately be `0` or `''`, and `x == null` used where `x === undefined` was meant. The first is a data-loss bug; the second is usually harmless but hides a modelling question the team has not answered — namely whether `null` and `undefined` both occur in that field, and if so, why. A schema or parsing layer that normalises absent values into exactly one representation at the boundary removes the question entirely, which is the better long-term fix. ## The trivia footnote Browsers specify one legacy host object, `document.all`, to compare loosely equal to `null` and `undefined` for web compatibility. It is a curiosity rather than a hazard — no real code branches on it — but it is the honest answer to "is `x == null` true for exactly two values", and mentioning it shows you read the algorithm rather than a blog summary. ## The one-line position `==` is banned because it is non-transitive and converts silently. The nullish check is the single case where its behaviour is exact, well-known and shorter than the alternative — so allow it if and only if the narrowness is enforced by a machine, and use `??` where you want a value rather than a branch.
- If x ?? fallback exists, is x == null still worth allowing?They solve different problems. `??` produces a value and is the right tool for supplying a default inline. `x == null` is a boolean, used for guard clauses, early returns and validation branches where there is no substitute value to produce. Keep `??` as the default for fallbacks and the comparison for control flow; neither replaces the other.
- How would you rewrite x == null if your team bans == with no exceptions?Write `x === null || x === undefined`, or extract it once into a small named helper such as `isNullish(x)` and use that everywhere. The helper is arguably better than the idiom: it reads as its intent, it is greppable, and it gives you one place to change if the codebase later needs to distinguish the two values.
- When does distinguishing null from undefined actually matter in production?Whenever a data source assigns them different meanings — a stored document where an absent key and a stored null differ, or an update payload where a null field means "clear this" and an omitted field means "leave it". In those systems the compact check erases the distinction. The durable fix is normalising absence to one representation at the parsing boundary.
saying these in an interview costs you the question
- x == null is just a shorter way to write !x
- It also matches 0, empty string and false
- Allowing it by convention is enough, no lint rule needed
- It works as a general check for empty values
- null and undefined are always interchangeable