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

JavascriptTypescript

Javascript Problem Overview


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?

Javascript Solutions


Solution 1 - Javascript

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:

  1. comparing different types with == can lead to unexpected type conversions.
  2. 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.

Solution 2 - Javascript

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.

Solution 3 - Javascript

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

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionGolo RodenView Question on Stackoverflow
Solution 1 - JavascriptlhkView Answer on Stackoverflow
Solution 2 - Javascriptuser886View Answer on Stackoverflow
Solution 3 - JavascriptIvan KleshninView Answer on Stackoverflow