Why does the TypeScript compiler compile its optional chaining and null-coalescing operators, ?.
and ??
, to
// x?.y
x === null || x === void 0 ? void 0 : x.y;
// x ?? y
x !== null && x !== void 0 ? x : y
instead of
// x?.y
x == null ? void 0 : x.y
// x ?? y
x != null ? x : y
?
Odds are that behind the scenes == null
does the same two checks, but even for the sake of code length, it seems like single check would be cleaner. It adds many fewer parentheses when using a string of optional chaining, too.
Incidentally, I'm also surprised that optional chaining doesn't compile to
x == null ? x : x.y
to preserve null
vs undefined
.
CodePudding user response:
You can find an authoritative answer in microsoft/TypeScript#16 (wow, an old one); it is specifically explained in this comment:
That's because of
document.all
[...], a quirk that gets special treatment in the language for backwards compatibility.document.all == null // true document.all === null || document.all === undefined // false
In the optional chaining proposal
document.all?.foo === document.all.foo
but
document.all == null ? void 0 : document.all.foo
would incorrectly returnvoid 0
.
So there is a particular idiosyncratic deprecated obsolete wacky legacy pseudo-property edge case of type HTMLAllCollection
that nobody uses, which is loosely equal to null
but not strictly equal to either undefined
or null
. Amazing!
It doesn't seem like anyone seriously considered just breaking things for document.all
. And since the xxx === null || xxx === undefined
version works for all situations, it's probably the tersest way of emitting backward-compatible JS code that behaves according to the spec.