> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/getify/You-Dont-Know-JS/llms.txt
> Use this file to discover all available pages before exploring further.

# Chapter 4: Coercing Values

> Master JavaScript's type coercion system - both explicit and implicit forms - and learn to write truly type-aware code

<Note>
  This book is currently a **work in progress**. Content is being actively developed.
</Note>

# Chapter 4: Coercing Values

We've thoroughly covered all of the different *types* of values in JS. And along the way, more than a few times, we mentioned the notion of converting — actually, coercing — from one type of value to another.

In this chapter, we'll dive deep into coercion and uncover all its mysteries.

## Coercion: Explicit vs Implicit

Some developers assert that when you explicitly indicate a type change in an operation, this doesn't qualify as a *coercion* but just a type-cast or type-conversion. In other words, the claim is that coercion is only implicit.

I disagree with this characterization. I use *coercion* to label any type conversion in a dynamically-typed language, whether it's plainly obvious in the code or not.

<Tip>
  **The line between *explicit* and *implicit* is not clear and objective — it's fairly subjective.** Keep that subjectivity in mind as we explore various forms of coercion.
</Tip>

### Implicit: Bad or ...?

<Warning>
  An extremely common opinion among JS developers is that **coercion is bad**, specifically, that **implicit coercion is bad**. The rise in popularity of type-aware tooling like TypeScript speaks loudly to this sentiment.
</Warning>

This feeling is not new. 14+ years ago, Douglas Crockford's book "The Good Parts" famously decried *implicit coercion* as one of the *bad parts*. Even Brendan Eich, creator of JS, regularly claims that *implicit coercion* was a mistake in the early design of the language that he now regrets.

However, here's an observation I've made over the years: **most of the folks who publicly condemn implicit coercion, actually use implicit coercion in their own code.**

<AccordionGroup>
  <Accordion title="Douglas Crockford's Code">
    Crockford says to avoid *implicit coercion*, but his code uses `if (..)` statements with non-boolean values evaluated.

    Many dismiss this, claiming that conversion-to-boolean isn't *really* coercion. But it absolutely is!
  </Accordion>

  <Accordion title="Brendan Eich's Endorsement">
    Brendan Eich says he regrets *implicit coercion*, but yet he openly endorses idioms like `x + ""` to coerce the value in `x` to a string. That's most definitely an *implicit coercion*.
  </Accordion>
</AccordionGroup>

<Tip>
  In fact, I think you'd be hard pressed to name any other well-known source of JS teaching that strongly endorses coercion (in virtually all its forms). I do — and this book definitely does! — but I feel mostly like a lone voice shouting futilely in the wilderness.
</Tip>

## Abstracts

Now that I've challenged you to examine coercion in more depth, let's first look at the foundations of how coercion occurs, according to the JS specification.

The specification details a number of *abstract operations* that dictate internal conversion from one value-type to another.

<Note>
  These operations *look* as if they're real functions that could be called, such as `ToString()` or `ToNumber()`. But by *abstract*, we mean they only exist conceptually by these names; they aren't functions we can directly invoke in our programs.
</Note>

### ToBoolean

Decision making (conditional branching) always requires a boolean `true` or `false` value. But it's extremely common to want to make these decisions based on non-boolean value conditions.

When non-boolean values are encountered in a context that requires a boolean — such as the condition clause of an `if` statement — the `ToBoolean()` abstract operation is activated.

<Steps>
  <Step title="Falsy Values">
    These values coerce to `false`:

    * `undefined`
    * `null`
    * `""` (empty string)
    * `0` and `-0`
    * `0n` (bigint zero)
    * `NaN`
  </Step>

  <Step title="Truthy Values">
    **Any other value** that's not in the falsy list is truthy and coerces to `true`:

    * `"hello"`
    * `42`
    * `[ 1, 2, 3 ]` (even empty arrays `[]`)
    * `{ a: 1 }` (even empty objects `{}`)
    * `"   "` (string with only whitespace)
  </Step>
</Steps>

```javascript theme={null}
// Abstract ToBoolean() (conceptual)
ToBoolean(undefined);               // false
ToBoolean(null);                    // false
ToBoolean("");                      // false
ToBoolean(0);                       // false
ToBoolean(NaN);                     // false

ToBoolean("hello");                 // true
ToBoolean(42);                      // true
ToBoolean([]);                      // true (even empty!)
ToBoolean({});                      // true (even empty!)
```

<Note>
  The `ToBoolean()` coercion operation is basically a lookup table rather than an algorithm. Thus, some developers assert that this isn't *really* coercion. I think that's bogus — it converts from non-boolean value-types to a boolean, and that's clear cut type coercion.
</Note>

### ToPrimitive

Any value that's not already a primitive can be reduced to a primitive using the `ToPrimitive()` abstract operation. Generally, it's given a *hint* to tell it whether a `number` or `string` is preferred.

```javascript theme={null}
// Abstract ToPrimitive() (conceptual)
ToPrimitive({ a: 1 }, "string");          // "[object Object]"
ToPrimitive({ a: 1 }, "number");          // NaN
```

<AccordionGroup>
  <Accordion title="How ToPrimitive Works">
    The operation looks on the object for either a `toString()` method or a `valueOf()` method. The order it looks for those is controlled by the *hint*:

    * `"string"` hint → check `toString()` then `valueOf()`
    * `"number"` hint (or no hint) → check `valueOf()` then `toString()`

    If the method returns a value matching the *hinted* type, the operation is finished. Otherwise, it tries the other method.
  </Accordion>
</AccordionGroup>

### ToString

Pretty much any value that's not already a string can be coerced to a string representation, via `ToString()`:

```javascript theme={null}
// Abstract ToString() (conceptual)
ToString(42.0);                 // "42"
ToString(-3);                   // "-3"
ToString(Infinity);             // "Infinity"
ToString(NaN);                  // "NaN"
ToString(42n);                  // "42"

ToString(true);                 // "true"
ToString(false);                // "false"

ToString(null);                 // "null"
ToString(undefined);            // "undefined"
```

<Warning>
  Some results may vary from common intuition:

  ```javascript theme={null}
  ToString(Number.MAX_VALUE);     // "1.7976931348623157e+308"
  ToString(-0);                   // "0" (hides the negative!)
  ToString(Symbol("ok"));         // TypeError exception thrown
  ```
</Warning>

#### Default toString()

When `ToString()` is activated with an object value-type, it delegates to `ToPrimitive()`:

```javascript theme={null}
ToString(new String("abc"));        // "abc"
ToString(new Number(42));           // "42"

ToString({ a: 1 });                 // "[object Object]"
ToString([ 1, 2, 3 ]);              // "1,2,3"
```

### ToNumber

Non-number values that resemble numbers can generally be coerced to a numeric representation, using `ToNumber()`:

```javascript theme={null}
// Abstract ToNumber() (conceptual)
ToNumber("42");                     // 42
ToNumber("-3");                     // -3
ToNumber("1.2300");                 // 1.23
ToNumber("   8.0    ");             // 8 (whitespace stripped)

ToNumber("123px");                  // NaN (not fully numeric)
ToNumber("hello");                  // NaN

ToNumber(true);                     // 1
ToNumber(false);                    // 0

ToNumber(null);                     // 0
ToNumber(undefined);                // NaN
```

<Warning>
  There are some rather surprising designations:

  ```javascript theme={null}
  ToNumber("");                       // 0 (surprising!)
  ToNumber("       ");                // 0 (surprising!)
  ```

  I think it would have made much more sense for these to coerce to `NaN`, the way `undefined` does.
</Warning>

Some primitive values are not allowed to be coerced to numbers:

```javascript theme={null}
ToNumber(42n);                      // TypeError exception thrown
ToNumber(Symbol("42"));             // TypeError exception thrown
```

#### Default valueOf()

When `ToNumber()` is activated on an object value-type, it delegates to `ToPrimitive()` with `"number"` as the hint:

```javascript theme={null}
ToNumber(new String("abc"));        // NaN
ToNumber(new Number(42));           // 42

ToNumber({ a: 1 });                 // NaN
ToNumber([ 1, 2, 3 ]);              // NaN
ToNumber([]);                       // 0 (empty array!)
```

### Equality Comparison

When JS needs to determine if two values are the *same value*, it activates various equality comparison operations.

<AccordionGroup>
  <Accordion title="SameValue()">
    The strictest equality comparison, with absolutely no coercion. Used by `Object.is()`.

    ```javascript theme={null}
    SameValue(42, 42);                  // true
    SameValue(NaN, NaN);                // true
    SameValue(0, -0);                   // false
    ```
  </Accordion>

  <Accordion title="SameValueZero()">
    Like `SameValue()`, but treats `0` and `-0` as indistinguishable. Used by `Array.includes()`, `Set.has()`, `Map.has()`.

    ```javascript theme={null}
    SameValueZero(0, -0);               // true
    SameValueZero(NaN, NaN);            // true
    ```
  </Accordion>

  <Accordion title="IsStrictlyEqual()">
    Used by the `===` operator. Similar to `SameValue()`, but:

    * `NaN === NaN` returns `false` (unlike `SameValue`)
    * `-0 === 0` returns `true` (unlike `SameValue`)
  </Accordion>

  <Accordion title="IsLooselyEqual()">
    Used by the `==` operator. If types match, delegates to `IsStrictlyEqual()`. If types differ, performs coercion (preferring numeric comparison) until types match.
  </Accordion>
</AccordionGroup>

#### Higher-Abstracted Equality

The `IsLooselyEqual()` operation for the `==` operator:

<Steps>
  <Step title="Same Types">
    If types match, immediately delegates to `IsStrictlyEqual()` (same as `===`).
  </Step>

  <Step title="Nullish Equality">
    If one value is `null` and the other is `undefined`, returns `true`. They're coercively equal to each other (and to no other values).
  </Step>

  <Step title="Numeric Coercion">
    If types differ, prefers to coerce both operands to numbers:

    * If one is `number` and other is `string`, coerce `string` to `number`
    * If one is `bigint` and other is `string`, coerce `string` to `bigint`
    * If one is `boolean`, coerce it to `number`
    * If one is non-primitive (object), coerce it to primitive with `ToPrimitive()`
  </Step>

  <Step title="Recursive">
    Each time a coercion is performed, the algorithm is recursively reactivated with the new value(s), until the types are the same.
  </Step>
</Steps>

### Relational Comparison

When values are compared relationally ("is one value less than another?"), the `IsLessThan()` abstract operation is activated:

```javascript theme={null}
// Abstract IsLessThan() (conceptual)
IsLessThan(1, 2, /*LeftFirst=*/ true);            // true
```

<Note>
  There is no `IsGreaterThan()` operation. Instead, the arguments to `IsLessThan()` can be reversed. The third argument (`LeftFirst`) preserves left-to-right evaluation semantics.
</Note>

Like `IsLooselyEqual()`, the `IsLessThan()` operation is *coercive*, ensuring that value-types match before comparison (preferring numeric comparisons).

## Concrete Coercions

Now that we've covered all the abstract operations JS defines for handling various coercions, it's time to turn our attention to the concrete statements/expressions we can use in our programs that activate these operations.

### To Boolean

To coerce a value that's not of type `boolean`, we need the abstract `ToBoolean()` operation.

<AccordionGroup>
  <Accordion title="Boolean() Function">
    The most explicit way to activate `ToBoolean()`:

    ```javascript theme={null}
    Boolean("hello");               // true
    Boolean(42);                    // true
    Boolean("");                    // false
    Boolean(0);                     // false
    ```
  </Accordion>

  <Accordion title="Double-Bang Idiom (!!)">
    More common among JS developers:

    ```javascript theme={null}
    !!"hello";                      // true
    !!42;                           // true
    !!"";                           // false
    !!0;                            // false
    ```

    The `!!` is two usages of the unary `!` operator. The first `!` coerces and negates, the second `!` flips it back.
  </Accordion>

  <Accordion title="Conditional Statements">
    Statements like `if`, `while`, `for`, and ternary `? :` implicitly coerce to boolean:

    ```javascript theme={null}
    specialNumber = 42;

    if (specialNumber) {
        // implicitly coerces to boolean
    }
    ```
  </Accordion>

  <Accordion title="Logical Operators (&&, ||)">
    For both operators, the lefthand expression is coerced to boolean for the decision:

    ```javascript theme={null}
    isLoggedIn = user.sessionID || req.cookie["Session-ID"];
    isAdmin = isLoggedIn && ("admin" in user.permissions);
    ```

    **Note:** Neither operator's final result is actually coerced to a boolean. They return one of their operands.
  </Accordion>
</AccordionGroup>

<Tip>
  **Which is more explicit?**

  `Boolean(..)` directly states what it's doing: converting to boolean. However, `!!` is more recognizable to JS developers. The "most explicit" depends on your perspective.

  My take: `Boolean(..)` is the most preferable explicit coercion form. I think `!!`, `if`, `for`, `while`, `&&`, and `||` are all *implicitly* coercing non-booleans, but I'm OK with that.
</Tip>

### To String

There are several ways to activate the `ToString()` coercion.

<Steps>
  <Step title="String() Function">
    The most explicit way:

    ```javascript theme={null}
    String(true);                   // "true"
    String(42);                     // "42"
    String(-0);                     // "0"
    String(Infinity);               // "Infinity"
    String(null);                   // "null"
    String(undefined);              // "undefined"

    String(Symbol("ok"));           // "Symbol(ok)" (allowed explicitly!)
    ```
  </Step>

  <Step title="toString() Method">
    Available on non-nullish primitives via auto-boxing:

    ```javascript theme={null}
    true.toString();                // "true"
    42..toString();                 // "42"
    -0..toString();                 // "0"
    Symbol("ok").toString();        // "Symbol(ok)"

    [1,2,3].toString();             // "1,2,3"
    ({ a: 1 }).toString();          // "[object Object]"
    ```
  </Step>

  <Step title="String Concatenation (+)">
    The `+` operator is overloaded to prefer string concatenation if either operand is already a string:

    ```javascript theme={null}
    true + "";                      // "true"
    42 + "";                        // "42"
    null + "";                      // "null"
    undefined + "";                 // "undefined"

    Symbol("ok") + "";              // TypeError! (implicit coercion disallowed)
    ```
  </Step>
</Steps>

<Warning>
  **String Concatenation vs String() Function:**

  A common misconception is that `String(x)` and `x + ""` are basically equivalent. They're not!

  * `String(Symbol("ok"))` works (explicit coercion allowed)
  * `Symbol("ok") + ""` throws TypeError (implicit coercion disallowed)

  JS intentionally discriminates between explicit and implicit symbol-to-string coercions. TC39 felt that symbols could too easily be mistaken as strings, so they wanted developers to express intent explicitly.
</Warning>

<Tip>
  Brendan Eich endorses `+ ""` as the *best* way to coerce values to strings. That carries weight in terms of him supporting at least a subset of *implicit* coercion practices!
</Tip>

### To Number

Numeric coercions are a bit more complicated since we can target either `number` or `bigint`.

<AccordionGroup>
  <Accordion title="Number() and BigInt() Functions">
    ```javascript theme={null}
    Number("42");                   // 42
    Number("-3.141596");            // -3.141596
    Number("-0");                   // -0

    BigInt("42");                   // 42n
    BigInt("-0");                   // 0n (no negative zero for bigint)

    Number("123px");                // NaN
    BigInt("123px");                // SyntaxError

    Number(true);                   // 1
    BigInt(true);                   // 1n

    Number(42n);                    // 42
    BigInt(42);                     // 42n
    ```
  </Accordion>

  <Accordion title="Unary + Operator">
    Commonly assumed to coerce the same as `Number()`, but has differences:

    ```javascript theme={null}
    +"42";                          // 42
    +"0b101010";                    // 42

    // But watch out:
    +42n;                           // TypeError! (implicit coercion)
    Number(42n);                    // 42 (explicit coercion)
    ```

    JS interprets unary `+` in front of a `bigint` as an *implicit* coercion (disallowed), but `Number(..)` as an *explicit* coercion (allowed).
  </Accordion>

  <Accordion title="Mathematical Operations">
    Mathematical operators coerce non-number operands to numbers:

    ```javascript theme={null}
    x - 0;                  // Coerces x to number
    x * 1;                  // Coerces x to number
    x / 1;                  // Coerces x to number
    ```

    **Note:** `x + 0` isn't as safe, since `+` is overloaded for string concatenation.
  </Accordion>

  <Accordion title="Bitwise Operations">
    Bitwise operators expect 32-bit integer operands:

    ```javascript theme={null}
    x | 0;                  // Coerces x to 32-bit integer
    ```

    This is one of several "type annotations" from ASM.js efforts.
  </Accordion>
</AccordionGroup>

<Warning>
  `Number(..)` and `+` are NOT interchangeable:

  ```javascript theme={null}
  Number(42n);                    // 42 (works)
  +42n;                           // TypeError (fails)
  ```

  I think `Number(..)` is the safer/more reliable form.
</Warning>

### To Primitive

Most operators in JS are designed to run against primitive values. When used against an object value, the abstract `ToPrimitive()` algorithm is activated.

Let's set up a spy object to inspect how different operations behave:

```javascript theme={null}
spyObject = {
    toString() {
        console.log("toString() invoked!");
        return "10";
    },
    valueOf() {
        console.log("valueOf() invoked!");
        return 42;
    },
};
```

<AccordionGroup>
  <Accordion title="String Coercion with Objects">
    ```javascript theme={null}
    String(spyObject);
    // toString() invoked!
    // "10"

    spyObject + "";
    // valueOf() invoked!
    // "42"
    ```

    **Surprise!** `String(..)` and `+ ""` are NOT equivalent:

    * `String(..)` provides `"string"` hint to `ToPrimitive()`
    * `+ ""` provides no hint (similar to `"number"` hint)

    Even though `+ ""` invokes `valueOf()` returning `42`, that value is then coerced to string `"42"`.
  </Accordion>

  <Accordion title="Number Coercion with Objects">
    ```javascript theme={null}
    Number(spyObject);
    // valueOf() invoked!
    // 42

    +spyObject;
    // valueOf() invoked!
    // 42
    ```

    Both perform the same `ToPrimitive()` coercion with `"number"` hint.
  </Accordion>

  <Accordion title="BigInt Coercion with Objects">
    ```javascript theme={null}
    BigInt(spyObject);
    // valueOf() invoked!
    // 42n
    ```

    If `valueOf()` returns a `number`, it can safely be coerced to `bigint`.
  </Accordion>
</AccordionGroup>

#### Overriding ToPrimitive

You can override the whole default `ToPrimitive()` operation by setting the special symbol property `Symbol.toPrimitive`:

```javascript theme={null}
spyObject6 = {
    [Symbol.toPrimitive](hint) {
        console.log(`toPrimitive(${hint}) invoked!`);
        return 25;
    },
    toString() {
        console.log("toString() invoked!");
        return "10";
    },
    valueOf() {
        console.log("valueOf() invoked!");
        return 42;
    },
};

String(spyObject6);
// toPrimitive(string) invoked!
// "25"   (not "10")

Number(spyObject6);
// toPrimitive(number) invoked!
// 25     (not 42)
```

<Warning>
  If the defined `Symbol.toPrimitive` function does not return a primitive value, an exception will be thrown. Make sure to always return an actual primitive value!
</Warning>

### Equality

The most obvious place where coercion is involved in equality checks is with the `==` operator.

#### Equality Operators: == vs ===

The `==` operator behaves extremely predictably, ensuring that both operands match types before performing its equality check.

<Steps>
  <Step title="Same Types">
    If the types of both operands are the same, `==` has the **exact same behavior** as `===`. It immediately delegates to `IsStrictlyEqual()`.

    ```javascript theme={null}
    myObj = { a: 1 };
    anotherObj = myObj;

    myObj == anotherObj;                // true
    myObj === anotherObj;               // true
    ```
  </Step>

  <Step title="Different Types">
    If the operand types differ, `==` allows coercion until they match, and **prefers numeric comparison**:

    ```javascript theme={null}
    42 == "42";                         // true
    ```

    Here, `"42"` string is coerced to `42` number (not vice versa), then the comparison is `42 == 42`, which returns `true`.
  </Step>
</Steps>

<Warning>
  **Common Myth Dispelled:**

  It's often said that only `===` checks the type and value, while `==` checks only the value. **Not true!**

  Both `==` and `===` are type-sensitive, each checking the types of their operands. The difference:

  * `==` allows coercion of mismatched types
  * `===` disallows any coercion
</Warning>

#### Nullish Coercion

`==` is the most obvious place that JS exposes nullish coercive equality:

```javascript theme={null}
null == undefined;              // true
```

Neither `null` nor `undefined` will ever be coercively equal to any other value in the language, other than to each other.

```javascript theme={null}
if (someData == null) {
    // `someData` is "unset" (either null or undefined)
}

if (someData != null) {
    // `someData` is set (neither null nor undefined)
}
```

Compare these two approaches:

```javascript theme={null}
if (someData == null) {
    // ..
}

// vs:

if (someData === null || someData === undefined) {
    // ..
}
```

<Tip>
  Both `if` statements behave identically. Which would you rather write and read?

  In performance benchmarks, the single `== null` check is *slightly faster* than the combination of two `===` checks. There's a tiny but measurable benefit to letting JS's `==` perform the implicit nullish coercion.
</Tip>

#### == Boolean Gotcha

<Warning>
  **The biggest gotcha to be aware of with `==` has to do with booleans.**

  Pay very close attention here! If you take my simple advice (at the end of this section), you'll never be a victim.
</Warning>

Consider this snippet, assuming `isLoggedIn` is NOT holding a `boolean` value:

```javascript theme={null}
if (isLoggedIn) {
    // ..
}

// vs:

if (isLoggedIn == true) {
    // ..
}
```

The first `if` statement activates `ToBoolean()` coercion. The second looks like it would behave the same way, but **it doesn't!**

The `==` operator doesn't invoke `ToBoolean()`. When types don't match, it prefers to coerce both to numbers.

Example with `isLoggedIn` as `"yes"`:

```javascript theme={null}
// (1) "yes" == true
// (2) "yes" == 1
// (3) NaN == 1
// (4) NaN === 1           // false
```

So `if ("yes")` passes, but `if ("yes" == true)` fails! Even worse:

```javascript theme={null}
// What value makes both forms pass?
if (isLoggedIn) { }         // passes when isLoggedIn is 1 or "1"
if (isLoggedIn == true) { } // passes when isLoggedIn is 1 or "1"
```

<Warning>
  **Simple advice to avoid this gotcha:**

  **Never, ever, under any circumstances, perform a `==` check if either side of the comparison is a `true` or `false` value.**

  When dealing with booleans, stick to implicitly coercive forms that genuinely activate `ToBoolean()`, such as `if (isLoggedIn)`.
</Warning>

## Coercion Corner Cases

That's not to say that coercion is perfect. There's several frustrating corner cases we need to be aware of.

### Strings

Array-to-string coercion is annoying:

```javascript theme={null}
String([1, 2, 3]);                // "1,2,3" (no brackets!)
String([]);                       // "" (can't tell it's an array!)
String([null, undefined]);        // "," (null/undefined disappear!)
```

Object-to-string coercion is almost as aggravating:

```javascript theme={null}
String({});                       // "[object Object]"
String({ a: 1 });                 // "[object Object]" (no help!)
```

### Numbers

<Warning>
  **The worst root of all coercion corner case evil:**

  ```javascript theme={null}
  Number("");                       // 0
  Number("       ");                // 0
  ```

  I still shake my head at this one. The empty string has nothing in it to determine a numeric representation. `0` is **NOT** the numeric equivalent of missing/invalid numeric value. That's what `NaN` is for!
</Warning>

Even worse, recall how `[]` coerces to the string `""`? By extension:

```javascript theme={null}
Number([]);                       // 0 (absurd!)
```

Other mildly annoying cases:

```javascript theme={null}
Number("NaN");                    // NaN (accidental!)
Number("Infinity");               // Infinity
Number("infinity");               // NaN (case sensitive!)

Number(false);                    // 0
Number(true);                     // 1
false + true + false + false + true;  // 2 (really?)
```

### Coercion Absurdity

To prove the point, let's take absurdity to level 11:

```javascript theme={null}
[] == ![];                          // true
```

How can a value be coercively equal to its negation!?

Follow down the coercion rabbit hole:

1. `[] == ![]`
2. `[] == false` (! coerces to boolean, negates)
3. `"" == false` (array to string)
4. `0 == false` (empty string to number)
5. `0 == 0` (boolean to number)
6. `0 === 0` → `true`

<Note>
  Three different absurdities conspiring: `String([])`, `Number("")`, and `Number(false)`. If any of these weren't true, this nonsense wouldn't occur.

  **This is NOT `==`'s fault.** It gets the blame, but the real culprits are the underlying `string` and `number` corner cases.
</Note>

## Type Awareness

We've now examined coercion from every conceivable angle. But what's the point of all this? Is it mostly just trivia?

Let's return to the observations I posed at the beginning of this chapter.

<Tip>
  **Key Insight:**

  No matter what you do, you won't be able to get away from the need to be aware of, understand, and manage JS's value-types and the conversions between them.

  **Type-aware programming is always, always better than type ignorant/agnostic programming.**
</Tip>

### Uhh... TypeScript?

Surely you're thinking: "Why can't I just use TypeScript and declare all my types statically, avoiding all the confusion of dynamic typing and coercion?"

<AccordionGroup>
  <Accordion title="What TypeScript Provides">
    TypeScript is both **statically-typed** (types declared at author time, checked at compile-time) and **strongly-typed** (variables/containers are typed, associations enforced, disallows implicit coercion).

    The greatest strength of TypeScript is that it typically forces both the author and reader of code to confront the types comprising a program.
  </Accordion>

  <Accordion title="What TypeScript Doesn't Solve">
    * TypeScript types are *erased* by the compiler
    * What's left is just JS that the JS engine has to contend with
    * You cannot fully rely on TypeScript types to solve all problems
    * TypeScript can't understand some specific situations
    * False positives: complaints about things which aren't actually errors
  </Accordion>
</AccordionGroup>

### Type-Awareness Without TypeScript

Does a dynamically-typed system automatically mean you're programming with less type-awareness? I disagree.

Consider this variable declaration:

```javascript theme={null}
let API_BASE_URL = "https://some.tld/api/2";
```

Is that statement in any way *type-aware*? Sure, there's no `: string` annotation. But I definitely think it IS still type-aware! We clearly see the value-type (`string`) of the value being assigned.

Later usage:

```javascript theme={null}
// Are we using the secure API URL?
isSecureAPI = /^https/.test(API_BASE_URL);
```

I know the regular-expression `test()` method expects a string, and since I know `API_BASE_URL` is holding a string, I know that operation is type-safe.

Similarly, since I know the simple rules of `ToBoolean()` coercion:

```javascript theme={null}
// Do we have an API URL determined yet?
if (API_BASE_URL) {
    // ..
}
```

<Tip>
  **Type-awareness in coding means thinking carefully about whether such things will be *clear* and *obvious* to the reader of the code.**
</Tip>

### Type Aware Equality

Let's revisit equality comparisons (`==` vs `===`) from the perspective of type-awareness.

I promised I would make the case for `==` over `===`, so here it goes.

<Steps>
  <Step title="Known Facts">
    1. If the types of operands for `==` match, it behaves **exactly the same** as `===`
    2. If the types of operands for `===` do NOT match, it will always return `false`
    3. If the types of operands for `==` do NOT match, it will allow coercion (preferring numeric type-values) until types match
  </Step>

  <Step title="Possible Conditions">
    When comparing `x` and `y`, we might be in:

    1. **Known types** - We know exactly what type(s) `x` and `y` could be
    2. **Unknown types** - We can't tell what those types could be

    Condition (1) is far preferable and represents type-aware code. Condition (2) represents type-unaware code.
  </Step>
</Steps>

#### (2) Unknown Types

If you're in scenario (2), **your code is in a problem state.** The best thing to do is... fix it! Change the code so it's type-aware.

If you cannot ensure type-awareness, you **must** use the `===` strict-equality operator:

```javascript theme={null}
if (x === y) {
    // ..
}
```

<Warning>
  You're only picking `===` as a last resort, when your code is so type-unaware (type-broken!) as to have no other choice.
</Warning>

#### (1) Known Types

If you know the types of `x` and `y`, there are two sub-conditions:

* **(1a)** `x` and `y` are of the same type
* **(1b)** `x` and `y` are of different types

##### (1a) Known Matching Types

If the types match, we know for certain that `==` and `===` do exactly the same thing. Except, `==` is shorter by one character.

```javascript theme={null}
// This is best
if (x == y) {
    // ..
}
```

An extra `=` would do nothing to make the code more clear. In fact, it makes it worse!

```javascript theme={null}
// This is strictly worse here!
if (x === y) {
    // ..
}
```

Why is it worse? Because `===` signals to readers that we're avoiding coercion. But we already know that no coercion would occur! The `===` might confuse readers, making them second-guess their understanding.

<Tip>
  **If you know the types of an equality comparison, and you know they match, there's only one right choice: `==`.**
</Tip>

##### (1b) Known Mismatched Types

We need to compare `x` and `y`, and we know their types are **NOT** the same.

If you pick `===`, you've made a huge mistake. Why? Because `===` used with known-mismatched types will **never return `true`**. It will always fail.

```javascript theme={null}
// `x` and `y` have different types?
if (x === y) {
    // Congratulations, this code will NEVER run
}
```

So `===` is out. We have two options:

* **(1b-1)** Change the code so we're not trying to compare known mismatched types (explicitly coerce one or both values so types match, then use `==`)
* **(1b-2)** If we're going to compare known mismatched types for equality, we **must** use `==`, because it's the only operator that can coerce operands until types match

```javascript theme={null}
// `x` and `y` have different types,
// so let's allow JS to coerce them for equality comparison
if (x == y) {
    // .. (so, you're saying there's a chance?)
}
```

#### Summarizing Type-Sensitive Equality Comparison

<Card title="The Case for == Over ===" icon="lightbulb">
  **The case for always preferring `==` over `===`:**

  1. Whether you use TypeScript or not, the goal should be to have every part of code be *type-aware*

  2. **If you know the types, you should always prefer `==`:**
     * When types match, `==` is both shorter and more proper
     * When types don't match, `==` is the only operator that can coerce operands, so it's the only way such a check could ever hope to pass

  3. **Only if you can't know/predict the types,** fall back to `===` as a last resort (and probably add a comment explaining why)
</Card>

<Warning>
  **TypeScript's Inconsistency Problem:**

  If you're using TypeScript properly and you know the types of an equality comparison, using `===` for that comparison is just plain **wrong**.

  The problem is, TypeScript strangely and frustratingly still requires you to use `===`, unless it already knows that the types are matched.

  Try this in TypeScript:

  ```typescript theme={null}
  let result = (42 == "42");
  // This condition will always return 'false' since
  // the types 'number' and 'string' have no overlap.
  ```

  TypeScript is telling a lie here. Of course `42 == "42"` produces `true` in JS!
</Warning>

## Summary

We've covered coercion from every angle - the abstract operations, concrete forms, corner cases, and type-aware usage.

<CardGroup cols={2}>
  <Card title="Key Takeaways" icon="key">
    * Coercion is unavoidable in JS
    * Type-awareness is crucial
    * `==` is preferable when types are known
    * Corner cases exist but can be avoided
  </Card>

  <Card title="Best Practices" icon="star">
    * Know your types
    * Use `==` for type-aware comparisons
    * Avoid `== true` and `== false`
    * Embrace coercion responsibly
  </Card>
</CardGroup>

<Tip>
  **The controversial conclusion:**

  With proper understanding, JavaScript's type system and coercion become powerful tools rather than sources of bugs. The key is not avoiding coercion, but understanding it deeply and using it wisely.
</Tip>
