Why to use strict comparison in string status checking? - javascript

I oftentimes see answers using strict comparison (===) instead of normal comparison (==) on status checking, i.e. here:
if(document.readyState === 'complete') ...
I would understand the reason if it were applied on empty string and the obtained value could be also other falsy value with different meaning. But when applied on non-empty string (like 'complete' in the sample), I believe the result is always the same for '==' and '==='. Is that so?
Some people measured that '===' can be faster, but I haven't seen a real world example where it would make any observable difference, so I don't take this micro-optimalization seriously.
On the other hand, anytime I see this operator, I read it as a warning "mind the type here!". But since document.readyState is always string, it annoys me that the original coder made me to study the code what other types there can appear - only to find out that only string.
To me, it is a strong reason to be polite to those who read my code and never use '===' when the type plays no role in the comparison.
Since '===' appears in similar cases in many SO answers and many expert pages, I would like to know if it is just social bandwagon or if there is any good reason why to use it in status checking.

I would recommend always using '===' instead of '==' when strict equality checking is required, which it is in most cases, for this reason: it declares intent. When I see code with '===', I will read it as 'a must be referentially or primitively equal to b'. When I see '==', I will read it as 'a must be coercibly equal to b'. From that, I will judge what kind of goal the code / original programmer is trying to accomplish and how they are passing data around to get the job done. Essentially, it yields insight into the context of the application, the way data is being passed around, and how this function / method / code block fits into the picture.
With that being said, if I see someone do 'a == b' when they are both strings, I'm not going to get on any high horse and make a fuss about it.

It always depends on what you want to achieve, and what you consider to be equal.
== is not always bad, but it often can lead to false assumptions.
If you have something like this:
class TestA {
toString() {
return 'complete'
}
}
class TestB {
valueOf() {
return 'complete'
}
}
let testA = new TestA()
let testB = new TestB()
console.log(testA == 'complete')
console.log(testB == 'complete')
Then testA == 'complete' might be exactly what you want, but because it does an implicit cast, you might do a false assumption about it being a string when it evaluates to true.
So if you later want to call something like substring on it, or passing it to another function that expects an actual string, then it might fail with an unexpected error.
Using === over == is mostly about maintainability, if you start to refactor code or if you look at older code and you see a == you always need to think about if you really want to have an implicit cast at that point or if it this was an accident. So you need to follow the code flow or check if it was documented.
Using an explicitly and === does not prevent you from doing other mistakes, but it keeps the code consistent.

Related

Why use triple-equal (===) in TypeScript?

In JavaScript, it's commonly seen as best practice to use === instead of ==, for obvious and well-known reasons.
In TypeScript, which is one to be preferred? Is there even one which is preferable to the other one?
IMHO, using === in TypeScript doesn't make sense, since comparison already only works on equal types, hence you won't have the (more or less funny) coercion game as in plain JavaScript. If you take aside compatibility to JavaScript for a minute, TypeScript could even get rid of ===, couldn't it?
short version:
== can do unexpected type conversions, in Javascript 1=="1" is true. The === operator avoids this. Comparing different types with === is always false.
The typescript compiler will emit an error message when you compare different types with ==. This removes the unexpected type conversions that can happen with == in Javascript.
This is a case where valid Javascript leads to an error message in the typescript compiler. The idea that all valid Javascript is also valid Typescript is a common misconception. This is simply not true.
longer version:
I think the accepted answer is misleading. Typescript actually does fix == vs === (as far as possible at least).
In Javascript there are two comparison operators:
== : When comparing primitive values, like numbers and strings, this operator will apply a type conversion before doing the comparison. 1 == "1" evaluates to true.
===: This operator does not do type conversions. If the types don't match it will always return false.
Also, both operators will compare reference types based on their references. Two separate objects are never considered equal to each other, even if they store the same values:
let a = {val:1};
let b = {val:1};
c = a;
a==b; // false
a===b; // false
a==c; //true
a===c; //true
So there you have the two common sources of errors in Javascript comparisons:
comparing different types with == can lead to unexpected type conversions.
comparing objects and arrays is based on references not values stored inside.
As the existing answer already says, Typescript is designed as a superset of Javascript. So it doesn't change the behaviour of these comparison operators. If you write == in Typescript, you get type conversions.
So how is this fixed? With the compiler. If you actually do write code that compares incompatible types with == it's a compiler error. Try compiling the following sample:
let str = "1";
let num = 1;
console.log(str == num);
The compiler will tell you:
comparisons.ts:4:13 - error TS2367: This condition will always return 'false' since the types 'string' and 'number' have no overlap.
4 console.log(str == num);
~~~~~~~~~~
Found 1 error.
It is a common misconception that any valid Javascript is also valid Typescript. This is not true and the code above is an example where the typescript compiler will complain about valid Javascript.
This fixes the first of the two sources of errors: unexpected type conversions. It doesn't deal with the second source of errors: comparisons based on references. As far as I know, when you want to do a comparison based on values stored by the objects, you simply can't use these operators. You'll have to implement your own equals() method.
Also, you may have noticed that the compiler error is wrong. The comparison will not always evaluate to false. I think this is a bug in typescript and have filed an issue.
Imagine you're designing TypeScript from scratch. Essentially, you're trying to optimize for making safer code easier to write (TypeScript design goal 1) with a few caveats which prevent you from doing everything you'd like.
JavaScript compatibility (TypeScript design goal 7)
JavaScript should be valid Typescript with no changes.
CoffeeScript makes no guarantees regarding this, so it can convert all instances of == to === and simply tell users don't rely on =='s behavior. TypeScript cannot redefine == without breaking all JavaScript code that relies on its behavior (despite this having sad implications for 3).
This also implies that TypeScript cannot change the functionality of === to, for example, check the types of both operands at compile time and reject programs comparing variables of different types.
Further, compatibility is not limited to simply JavaScript programs; breaking compatibility also affects JavaScript programmers by breaking their assumptions about the differences between == and ===. See TypeScript non-goal number 7:
Introduce behaviour that is likely to surprise users. Instead have due consideration for patterns adopted by other commonly-used languages.
JavaScript as the target of compilation (TypeScript design goal 4)
All TypeScript must be representable in JavaScript. Further, it should be idiomatic JavaScript where possible.
Really though, the TypeScript compiler could use methods returning booleans for all comparisons, doing away with == and === entirely. This might even be safer for users: define a type-safe equality method on each TypeScript type (rather like C++ operator==, just without overloading).
So there is a workaround (for users comparing classes). unknown or any variables can have their types narrowed before using the type-safe equality method.
Which to prefer
Use === everywhere you would in JavaScript. This has the advantage of avoiding the pitfalls common to ==, and doesn't require you to maintain an additional method. The output of the TypeScript compiler will be close to idiomatic JavaScript. Using == has very much the same pitfalls as JavaScript, particularly when you have any, [], or {} involved. As an exception, using == null to check for null or undefined may save headaches if library code is inconsistent.
A method for reference equality (behavior like === for classes) could be confused with a deep/value recursive equality check. Furthermore, === is widely used in TypeScript, and making your code fall in line with conventions is usually more important than any small bit of type safety.
Your intuition was correct. There's no sense to use === in TypeScript to imitate an equality check. The argument that TS compiles to JS "so you should use what is better in JS" is not valid. Why? Because Typescript ensures that both operands of comparison operator(s) have the same type. When both operands have the same type == and === behave identically. And by "identically" I mean 100% identical not just "alike". So there's no more correct or less correct version when both behave exactly the same way in JavaScript.
I guess other commenters here are just looking for ways to preserve their habit of using === or in other words to rationalize. Unfortunately, pure logic tells otherwise: there's no sense to replace == with ===, unless you're going to modify generated JS code manually which is probably never the case.
I use === exclusively for identity checks (when you compare x to x – the same variables, it's sometimes necessary in library code related to memoization). And my counter of errors related to eqeq operator shows 0.
Example:
const s : string = "s"
const n : number = 1
console.log(s == n)
TS2367: This condition will always return 'false' since the types 'string'
and 'number' have no overlap
My opinion is that one should always use ===.
First line of reasoning: TypeScript does not change == to ===. There're TypeScript translators which just strip types. So using === everywhere leads to more robust code if for some reason you forgot to type check your program or if you (or future maintainer of your code) used type casts to override type safety. Should not happen but many things should not happen.
Second line of reasoning: null == undefined. This is true with TypeScript as well. I think that if one writes if (x == null) it makes code less readable because it implies check for undefined as well and implicit code is less readable than explicit if (x === null || x === undefined). Also subtle bugs might occur if this is not done on purpose.
I don't see any issues when just using === everywhere unconditionally other than aesthetic preferences.

How should we name predicates for negative conditions?

I'd like to know which is considered a best practice when naming predicates in a negative condition.
I'm familiar with the convention of prepending is to the name of a predicate function to express that it returns a true or false value. For example, isGreaterThanFifty(120) clearly expresses the intent of the predicate function, that it will return true if the condition is met. Conversely, should we say !isGreaterThanFifty(120) when we want the negative condition, or would it be better to say isNotGreaterThanFifty(120)?
Should we create a predicate function that expresses:
the positive case (e.g. isValid()) and then use it with the ! (e.g. !isValid()) to negate it when using it
the negative case (e.g isInvalid() or isNotValid()) since that is how we are using it
For example,
if (!isValid(data)) {
return new Error();
}
// do something if it is valid
compared with
if (isNotValid(data)) {
return new Error();
}
// do something if it is valid
I'm looking for code that is more readable and expresses it's intent clearly.
I'm aware that some languages like Ruby allow us to use unless instead of if conditions or even append ? to the end of a function, but most languages do not. So my question is geared towards those languages like JavaScript that do not have such syntax.
There isn't a convention around this that I am aware.
And it should be driven by your actual need, for example are you going to make this call from more than one place (where it actually would benefit the code)? And are you possibly putting a bunch of predicates together, this could affect readability for example: (!isValid()) looks similar to (isValid()) which is why some prefer to use the full condition: (isValid() == false).
Or better yet putting the false on the left which can help prevent accidentally re-assigning a value when attempting to do a condition:
ie: false == isValid vs isValid == false which can be typo-ed or accidentally be replaced by a single = which would be bad news bears.
Use necessity as your driver and also always consider the future you (coming back to the code) and of course, always think of your Teammates.
I find negative names such as isNotValid confusing.
It is OK to use a negative name for a function in a negative context, but if isNotValid() is later used in a positive check, than you get !isNotValid(data). I find double negativity very confusing when reading other peoples code. So, to support future use and positive usage as well I'd go with isValid

Javascript testing whether or not a variable is set

Generally, I test whether or not a variable is set with something like this:
if (variable !== '') {
do something...
}
I know there are other methods for testing variables like typeof but I don't see any advantage - is this an appropriate way to test whether or not a variable is set? Are there problems with it that I should be aware of ?
Two reasons:
1) What if the variable is set by getting the contents of an empty input box?
if(someScenario){
var variable = $('empty-box').val(); }
Perhaps this is only done in certain cases, like when someScenario is true. Later on, you want to check if that variable was set. Your means returns false rather than true. Point is, you can come up with scenarios where you get wrong answers.
There's just no reason not to do it the accepted way.
if(typeof variable !== 'undefined')
It's no slower, has no real flaws, and is only a few characters more.
2) And most importantly, using typeof makes it totally clear what you're asking. Readability is crucial, and if another programmer read the first code, they would think you were checking that it wasn't an empty string. The method using typeof makes it perfectly clear what your conditional is looking for, and reduces the odds of mistakes later on.
If variable has been declared but might not have a value then your code:
if (variable !== '') {
tests if it is not the empty string. Is that what you want? An empty string might be a valid value. Better to test for undefined, or explicitly initialise it to a value that you can then treat as "invalid" (perhaps null, or whatever suits).
If variable has not been declared at all the above code would result in an error such that execution would stop at that point - you can't test the value of a variable that doesn't exist. So if, for example, you're trying to test a global variable that is created inside a function that may not have been called yet, or perhaps you're using several JS files and one needs to test a variable that may or may not have been created by one of the other files, then the only way to do it is with:
if (typeof variable != "undefined") {
Since you're using strict equality testing, the following will all return true:
false
undefined
null
0
The only time your check will return false is when you pass in an empty string.
Is that what you want?
Check out coffeescript's existential operator, by searching "The Existential Operator" on this page: http://coffeescript.org/
The functional problem with your approach is that is that you may inadvertently assign a blank string to variable at some point prior in your script and your logic block will now do the wrong thing.
From a stylistic standpoint your solution is less desirable because your intent to check the existence of the variable is not clear. Someone who was just reading through your code for this the first time might misunderstand what you wrote to mean "I'm expecting there to be a variable named variable set to the blank string" as opposed to "Do something if this variable does not exist."
This might be highly subjective, but my recommendation is to avoid code, that needs to check, whether a variable is set (a.o.t. has some value or type).
Consider this snipplet
var a=false;
if (some_condition) a="Blah";
if (typeof(a)=='string') ....
if (a===false) ...
this makes sure, a is always set, while keeping it easily differentiable from '', null or 0

Is this line from Underscore.js doing equality checking actually necessary?

I've just been looking at the _.isEqual function of Underscore.js and a section of the code goes something like this:
if (a === b) return true;
if (typeof a !== typeof b) return false;
if (a == b) return true;
I'm just wondering if there's any case where the third statement could be reached and evaluate to true?
Edit: Just to be clear, this isn't my own code I'm talking about, I'm reading the source of Underscore, in particular, this line and I was curious about why they're doing that.
I've just been browsing through the Underscore repo and came across a short discussion where someone asked the exact same thing, and it looks like it is actually unnecessary.
Following the algorithm defined by the ECMAScript Language Specification in section 11.9.6 and section 11.9.3 seems to show that no pair of values should return true in the above case.
So, in short, no, that situation is not possible.
The only situation where == and === react unexpectedly is when comparing a literal string ("123") to a constructed string (new String("123")), which would fail the first test.
However, on the second test it gets caught because the constructed string has the type object, but the literal has the type string.
Based on that, I'd say no, the third statement can never be reached, and evaluate to true.
When you use the == operator and the expressions are of different types, JavaScript will generally convert the two into the same type before comparing.
For example, this can happen with null and undefined. null == undefined is true, even though null === undefined is false. However typeof null is "object", while typeof undefined is "undefined". So, in this case you should return false on the second statement.
You can read all the details in the spec (section 11.9.3), it is very involved:
http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-262.pdf
My initial guess was that it was to work around a broken browser implementation.
However after digging into the git logs for that file it looks like the corresponding line was in the very first underscore.js checkin. (I'm not gonna hunt in the parent documentcloud core.js repo...) You can see it at line 334 of https://github.com/documentcloud/underscore/commit/02ede85b539a89a44a71ce098f09a9553a3a6890 .
So now my guess is that its just cruft that got left in, never completely tested and never cleaned out.

Why use === when you are sure types are equal?

It seems that the strict equality operator is preferred whenever possible - I put my code in JSLint and got the following feedback.
Code:
function log() {
console.log(arguments.length == 1 ? arguments[0] : arguments);
}
Feedback from JSLint:
Problem at line 2 character 34: Expected '===' and instead saw '=='.
I am curious to know what advantages === has over == here. Basically, .length returns a Number, and 1 is a Number as well. You can be 100% sure, so === is just a redundant extra token. Also, checking for the type whilst you know the types will always be the same has no performance advantage either.
So what's actually the reason behind using === here?
The only reason is that you don't have to think whether the comparison you are doing will involve coercion or not. If you stick to using ===, you just have one thing less to worry about.
Of course not everyone agrees. This is why you can disable specific checks in JSlint, if you are sure of what you are doing.
It's not strictly required, as you know the length function is well-tested. But what if length was a bit buggy, and there was a chance it could return false?
In this situation, both 0 and false would be evaluated as false using the == comparison. To test for false (when there is also the chance of 0 being returned), you need to also check the data type, which is where === comes in.
Well, === is supposed to be infinitesimally faster, as it can fail faster (on type mismatch), but that's deep inside the "pointless microoptimizations" territory.
Realistically, I'd advise for ===, even if you are 105% sure - I've been tripped up by my assumptions too many times ("yes, I am positive they are the ... same ... uh ..."). Unless there's a good reason to turn them off, it is better to keep sanity checks enabled in code than trust to do them in your mind.

Categories