JavaScript Equality Minesweeper
slikts.github.io
slikts.github.io
The alternatives are writing `x === null || x === undefined` everywhere (which might lead to people just picking one out of laziness, leading to bugs), doing things like `if (x)` (which we still do sometimes, but can lead to subtle falsy-related bugs if you're not careful), or to do careful bookkeeping of when variables might be null vs undefined. In my opinion, the null/undefined distinction is more cognitive overhead than the benefit it provides, so it's best to just check for both at the same time and never intentionally treat them as different. Given that mindset, I think the `== null` pattern is slightly unfortunate, but better than the alternatives.
`const isString = myVar => typeof myVar !== 'string'`
This is more reusable and more idiomatic in my opinion than null/undefined checks
See this example https://goo.gl/AyoC3T
For those not aware, you can specify a rule in [js/es/ts]lint to allow only these cases.
It's like every time you defensively throw in a nil-guard just in case: everyone now has to go "wait, can nils really get this far into the system?"
You just start wasting the time of the people experienced enough to identify it as a potential problem.
if (myNum === 2 || myNum === '2') {}
It's very easy for developers to mistakenly "see" the third equality symbol and get confused by the intent of the code.
if (Number(myNum) === 2) {}
For me this also extends to using `Boolean(val)` instead of `!!val`, etc.; though I understand the appeal of those nifty one-liners, I think they cause confusion in many places and don't communicate intent nearly as well.* you don't actually see the case-space (value space) of all the comparisons that do work as expected, and
* you don't a sense of what is the likelihood that these sort of comparisons would happen in real world code
Some of them like the empty string are likely to happen from user input, but Typescript mitigates those by forcing you to e.g. use Number(inputField.value) to conver to number and complaining about the assignment otherwise.
Others pretty much never ever happen - instead of comparing 1 or -1 to true, you're more likely to use if (val) which casts to boolean, and the truthy table is different from the equality comparison table (it makes a bit more sense)
Most of the real world comparisons are to non-empty strings or numbers, and those are only equal to arrays in some cases - but its rare for an actual array to be produced by anything. Things you know are arrays already you don't compare using "==" to begin with.
So yeah, in practice the confusing rules of JS equality comparison don't really matter all that much.
I run into this all the time. Plenty of junior devs I work with do this. Unfortunately we haven't implemented typescript yet. Yea === solves this problem but I wish there was a deprecation path for ==. Why can't we figure out as a community how to deprecate horrible javascript apis? Why can't we as a community figure out how to have a good standard library?
IMO TypeScript fits like a glove on top of JS and largely gets rid of the language problems, leaving mainly library / ecosystem problems.
Dart did many things wrong, but one thing it did do right was the standard library. If we had a standard library like that in JS, that would change everything.
Another serious problem are modules. The fact that the ES6 loader is "open ended" just means that we don't really have a solution to the problem of distributing JS. The fact that HTTP2 push is somewhat broken means we can't rely on it to load ES6 modules either.
At the very least we need the concept of absolute and relative module identifiers. Even if the specifics are implementation-defined, e.g.
* whether module ids are used,
* or content hashes,
* or absolute paths (or maybe npm module names with the version?)
the ability to provide absolute modules via a DLL:
provides 'lodash@2.0.5' {
export ... // multiple things
}
provides 'other-module@version' {
}
would be of incredible help.That, or maybe we can fix HTTP2 push.
I mean, JS definitely has serious problems, but equality coercion is not one of them :)
I’m not one to pride myself on ignorance, but the JS equality operator is ridiculous and therefore, IMO, not worthy of the mental energy it demands.
> How well do you know the rules for the == operator in JavaScript?
Well enough to use `===`. I’ve noticed in my code every time I’m tempted to use `==`, I always end up finding a better way. `==` is basically code smell that only really smart people should use.
And then cross your fingers that no one else ever has to look at or work on that code because they might not pick up on whatever "smart" reason the == operator was used.
So with TypeScript code, I've been continuing to use == as in most other languages, with the expectation that any odd comparisons will be flagged by the compiler. On the other hand, I've not verified myself that it catches them all, so I wonder if anyone has come across other edge cases with this that could cause problems down the line.
I can think of one instance where I needed identity instead of equality, and I took the time to refactor to ensure that equality would work.
If you are curious, please post some real life real working code where in your mind usage of === is absolutely needed and I try my best to explain how I would tackle that with ==.
That's not dynamic typing, that's weak typing.
Dynamic typing just means the types are omitted in the source code, but they're still there. typeof 3 is still 'number'. If you explicitly specify the types, you get TypeScript.
But weak typing means you can do things with disparate types that you can't normally do, usually by implicitly coercing one to the same type as the other.
The world has generally considered weak typing a very bad idea. Type coercion has too many edge cases and unintuitive/unexpected combinations. Even Lua is experimenting with runtime flags that disable type coercion such as "1" + 2. (That's the reason they have the .. operator btw.)
The only byproduct of weak typing that most people agree on keeping in modern languages is the concept of "truthy", but even then, languages have different ideas of what should be truthy. Clojure and Ruby say everything except nil and false are truthy, Python says "" and [] are also falsey, and I don't remember where JavaScript fully stands on this, but I know that null, undefined, false, and "" are at least falsey.
Now that is the type of thing that screams for supporting evidence.
> Type coercion has too many edge cases and unintuitive/unexpected combinations.
That depends on the implementation. I would say Perl handles this problem neatly by making the coercion done entirely based on the operator used, so it's always obvious.
I think the direction of mainstream programming languages over the past 20 years is overwhelmingly clear about this.
There's only one language that I know of that's still in popular use and that likes weak typing.
> That depends on the implementation. I would say Perl handles
Yep that's the one.
As much as I wish it wasn't so, I believe PHP sees more activity currently than Perl, and it's also weakly typed.
Javascript itself is weakly typed as well, which is why we're all having this discussion.
I would say that two of the most popular languages (probably the two most popular new languages) of the last two decades have been weakly typed.
I think you're expending your energy for the wrong reasons. There is no need to be "clever" when writing code. Being explicit is always better. Being explicit is how bugs are avoided, how new people can enter your codebase, and how you can maintain your codebase in the future. If you spend time thinking about how to justify usage of something in your code, it has no justification being used.
How's your score on the minesweeper? :)
I won't outright dismiss your coding style because I don't know what your codebase looks like and maybe you're genuinely talented JS devs who know what they're doing (something I'm decidedly not) but your comment makes it sound like discouraging the use of '==' is not "embracing" JS's dynamism. I don't think I agree with that, actually there are a bunch of highly dynamic languages where comparison is not such a mess. The main problem with '==' is that it's highly inconsistent and intransitive (as showcased by TFA). It's tricky to figure out what evaluates to true or false when types don't match. It's not PHP bad but it's pretty damn bad.
I don't think using equality is inherently bad. I think that it can lead to a lot of bad habits for junior devs who don't understand some basics about types. I'm not against weak/implicit typing systems, but my experience has taught me that junior devs (and student alike) are prone to errors extending from the type system.
Ok, but why? Do you not think there's an advantage in having a difference between strings and numbers?
"1" == 1?
You can chose that if you want, surely, but what are the advantages of 'embracing' this?
For example in Node.JS callback convention the last argument is always the callback functions, even if the function takes 4 arguments, you can put the callback in the 3:rd argument and it will figure out which one is the callback function. Then the first parameter in the callback function is either null or an Error.
Another example is RegeExp where you can write if( str.match(/foo/) ) instead of if ( str.match(foo) === null )
It's also convenient that empty strings are "falsy" which allow you to write if(!foo) instead of if( foo==="" || foo===undefined || foo===0 )
Also - you can use the == 'falsiness' whilst still in a strict scenario.
If you take the typing argument one step further, into Typescript, the advantages of just some basic typing become considerably more clear as the compiler/transpiler picks up loads of problems that wouldn't be found otherwise.
var foo = [];
foo.push["bar"];
You could say it's a type error. It's however not caught by neither TypeScript or Flow.let x:number[] = []; x.push("x");
Here, the transpiler will catch the error of trying to push a string to a number array.
Typing is your friend, and it will in almost all cases help you write cleaner and safer code.
That said, if you mix types between comparison operands or inside arrays then you probably have bigger problems than "==" semantics. :-)
A lot of people seem to have a perception of languages as being handed down by some language gods, and apparent problems with the language get explained away by "mere users" lacking knowledge, perspective, discipline, etc.
And the engine makes it as fast as === https://github.com/dperini/nwsapi/pull/11#issuecomment-38327... (Generally else == is slower than ===)
e.g.:
- say there are 100 or so blocks, 20 are checkmarks
- if I click on 40, and get 10 checks, 30 blank, that means I got 30/20 wrong
The only legit case to use == is if you are
1. Insane
2. The kind of person who changes Java 1 object to equal 2
I wrote tens of thousands of lines of JS (e.g. this library https://github.com/photopea/UPNG.js ), I never used "===" in my life :D
Also, from a brief glance at your code example, you don't seem to have caught up with other current best practices like linting or modules.
The type information simply is not there.
does it not make sense to just remove it from the language completely? who is deciding that?
if (someValue != null) {
// someValue is neither null nor undefined
}
> does it not make sense to just remove it from the language completely?No, because you'd wreck quite a bit of the internet with that.
> who is deciding that?
ECMA TC39
The way syntax is "removed from the language" in JS is by introducing commonly-used lint rules that disallow them, and that has already happened with `==`: https://eslint.org/docs/rules/eqeqeq . As long as the community makes it so a standard project setup has a linter with good defaults, `==` can effectively be removed from JavaScript without having to break nearly every website in existence (which is what would happen if browsers decided to start crashing on `==`).
The best solution currently is to have linter rules enforcing ===.
I would say that's the defining characteristics of JavaScript.
It makes it fun to write JavaScript with its quirkiness and "dynamic" nature. And once you master it you become a ninja.
Compare it to Java or Golang where you are restricted by the language syntax and anything slightly creative and fun is either not supported or not idiomatic.
nope. none of that. there are plenty of fun, dynamic languages that aren't the horrible pile of shit that is raw javascript.
> And once you master it you become a ninja.
wait, was this sarcasm? hard to tell..
Sure! For starters, you can write
if (foo == null) { ... }
Because `undefined` gets coerced to `null`, it saves you from writing: if (foo === null || foo === undefined) { ... }
And, because it's shorter, it's even allowed by [some JS linters](https://standardjs.com/).There you have your JS ninja trick to handle two distinct null types^TM .
1. I was making a generalized statement around JS, not specifically '=='. That's a misinterpretation of my statement.
>"my programming language is perfect"
2. I did not say JavaScript is perfect. That's a misinterpretation of my statement.
>"if you don't like it you're just too dumb to get it"
3. I did not say "if you don't like you're just too dumb." That's a misinterpretation of my statement.
JavaScript really _isn't_ all that expressive.
I just don't get it. What's wrong with expressing my love for a language that I love? I am not even asking for anyone to switch to JS, just expressing my feel.
Thanks for pointing that out.
I think might be due to the downvotes somehow. It makes you wonder if you did something wrongly or it is simply people disagreeing with you.
Yes, a ninja of the "undefined is not a function" wars.
In my experience, code that is "slightly creative and fun" is unreadable and not actually useful for creating working products as a part of a team.
[0]https://www.simplethread.com/dont-be-clever/
NaN == NaN
is falsy, no? (Shows as truthy in the game, unless I am misunderstanding.)But when I played the game, it looked to me like the game correctly claimed it is falsy. (Though I'm not 100% sure I know what the notation the game uses means.)
Wut?
> true == 1
-> true
> false == 0
-> true
Do you get something different? Or is the game claiming something different?