75

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?

Golo Roden
  • 140,679
  • 96
  • 298
  • 425
  • 1
    Since Typescript is compiled javascript, I think the `===` makes sense. – Vasileios Pallas Jul 20 '19 at 14:38
  • Oh, okay – this means that the TypeScript compiler does *not* turn `==` into `===` automatically, as e.g. CoffeeScript does? – Golo Roden Jul 20 '19 at 14:40
  • 3
    If you take away compatibility with JavaScript, a lot of things could be improved, but I think the designers of TypeScript value keeping it as close to JavaScript as possible, while still adding typing. – kshetline Jul 20 '19 at 14:40
  • 3
    I think it doesn't turn the `==` to `===` because Typescript also has a type `any`. – Vasileios Pallas Jul 20 '19 at 14:41
  • @VassilisPallas This makes sense. If you turn your two comments into an answer, I will happily accept it. – Golo Roden Jul 20 '19 at 14:42
  • 8
    This feels like a subjective/opinion question so I don't know that it can possibly have an authoritative answer. My opinions: • TypeScript is meant to support good JS practice, as valid JS code is valid TS code (with possible warnings) and therefore is always emitted as-is (unless targeting an older version of JS), so you pretty much want to keep `===` which is both valid JS and considered best practice. • If you have two expressions of type `string | number` you can compare them in TypeScript with `==` and you might still be surprised when `"3"==3` is true. – jcalz Jul 20 '19 at 14:44
  • I didn't know that about CoffeeScript. Interesting! If I had my way, I'd use a single `=` for comparison, and `<-` or `←` for assignment, and add a lot more Unicode support for things like `≤` and `≥`. – kshetline Jul 20 '19 at 14:44
  • @jcalz I have hoped that the answer to this question is not subjective or opinion-based, e.g. because there is an official recommendation by the TypeScript team, or something like that, with a reasonable explanation. – Golo Roden Jul 20 '19 at 14:45
  • Another opinion in favor of `===`... if I were getting rid of something, I'd get rid of `==`. Even if you think strong typing makes it harder to misuse `==`, it's still safer to use `===`. It would be like asking "why drive safely if you're wearing a seat belt?" – jcalz Jul 20 '19 at 14:49
  • 3
    @jcalz, the hypothetical proposal here was to get rid of `===` by making TypeScript's `==` *mean the same thing* as JavaScript's `===`, so that `==` would transpile to `===`. It wasn't a proposal to rely on type checking while using the same old meaning of `==`. – kshetline Jul 20 '19 at 14:54
  • 1
    Well, if that's the proposal, then one can point to [TypeScript's Design Goal](https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals#goals) #7 as an authoritative reason why that can't happen... runtime behavior of JS code should be preserved by TS, which means if you type `(Math.random() < 0.5 ? 3 : "3") == 3;` in TS, the resulting JS had better always evaluate to `true` at runtime. – jcalz Jul 20 '19 at 15:08

4 Answers4

73

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.

lhk
  • 27,458
  • 30
  • 122
  • 201
26

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.

user886
  • 1,149
  • 16
  • 16
  • 2
    "JavaScript should be valid Typescript with no changes." Not quite. Define "valid". Even though it will emit the same code, it is allowed to give you errors if you enable strict checking. The design goal only states that the runtime doesn't change. Nothing is guaranteed about the build time. – Vanuan Sep 28 '20 at 14:11
  • 1
    Could you please demonstrate an example of using "==" improperly that is allowed by TypeScript? – Vanuan Sep 28 '20 at 14:15
  • "Use === everywhere you would in JavaScript" - that only works if you come into typescript from having done Javascript before. Java or C# programmers getting into this might go for typescript over Javascript right away since it has more of the familiar type rigidity they're used to. – Nyerguds Jan 07 '22 at 10:41
4

Your intuition was correct, === adds little value in TypeScript. The argument about "compilation to JS" is not really valid. TS ensures both operands have the same type. And when both operands have the same type == and === behave identically. For 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

So, unless your code is scattered with anys or you're going to modify JS after a compilation, you won't gain benefits simply by replacing 2eq with 3eq.

People are just looking for ways to preserve their habit of using ===, to rationalize. But it's a good practice to reconsider your principles from time to time.

I can tell from my own experience that I've never had any problems with ==, even in JS. This "gotcha" was overemphasized by people coming to JS from other languages, but it's not really that important in practice. Trading false positives to false negatives or vice-versa doesn't make code automatically better.

You can finally discover TOP authors, like Ryan Florence or Kyle Simpson, using both == and === in their code – whatever fits a specific case. So don't blindly follow the crowd of "best-practicioners" and default linter settings. They were wrong so many times.

Ivan Kleshnin
  • 1,667
  • 2
  • 21
  • 24
-1

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.

vbezhenar
  • 11,148
  • 9
  • 49
  • 63