# JavaScript and Type Narrowing Magic

Type Narrowing allows the IDE (development environment) to narrow down the object type, from a union type, to a more specific type.

## Bullsh\*t Example

In this bullsh\*t example, `value` is the union type `string | number`, meaning it can either be a `string` or `number` (ya, please don’t do that).

![const value: string | number](https://cdn.hashnode.com/res/hashnode/image/upload/v1744822536520/86c1ec6e-71ce-485f-bf14-8499b5240ea0.png align="center")

The type can be narrowed down by checking the type with an `if`. Now that the IDE knows the type, it also knows `.toUpperCase()` only exists on `string` and `.toFixed()` only exists on `number`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744822271328/8c8b4baa-b177-4e34-8d87-072cbac946d2.png align="center")

## Type Narrowing an API Response

In this example, `fetchData` can respond with either a `Result` or `ErrorMessage` type. We can use an `if` again to narrow the type down. The IDE now knows inside the `if`, `data` is always of type `Result` and inside the `else`, it must be of type `ErrorMessage`.  
  
The IDE now gives us proper `Property does not exist` error messages when used on the wrong type.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744823031014/9f025338-f3dd-46ac-8b78-6395af8adf80.png align="center")

## Strict Null Checks

When Strict Null Checks are enabled, we can narrow down `data.message` type from a `Nullable string` to `string`. The error seen on line `17` doesn’t appear on line `20` because the `if` narrows down the type.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744823727291/666d8cad-1642-4590-86f4-6abb1bf58339.png align="center")

## Type Narrowing Magic in Custom Classes

Type Narrowing can also be controlled in custom classes. Inside the `if`, the IDE knows `res` is of type `Success` and in the `else` it is type `Failure`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1744824216522/3ec66afc-5cd1-4f9d-997e-6bbff57eaea7.png align="center")

The type Narrowing is controlled through the JSDocs on the `isOk()` function like this:

```javascript
export class Success {
  /** @returns {this is Success<T>} */
  isOk() {
    return true;
  }
}

export class Failure {
  /** @returns {this is Success<any>} */
  isOk() {
    return false;
  }
}
```

Here’s the full source to the above example:

```javascript
// @ts-check

/**
 * @template T
 * Represents a successful result.
 */
export class Success {
  /** @param {T} value */
  constructor(value) {
    /** @private */
    this._value = value;
  }

  /** @returns {this is Success<T>} */
  isOk() {
    return true;
  }

  /** @returns {T} */
  value() {
    return this._value;
  }
}

/**
 * @template E
 * Represents a failed result.
 */
export class Failure {
  /** @param {E} error */
  constructor(error) {
    /** @private */
    this._error = error;
  }

  /** @returns {this is Success<any>} */
  isOk() {
    return false;
  }

  /** @returns {E} */
  error() {
    return this._error;
  }
}

/**
 * Result class with static helpers for Success and Failure.
 */
export default class Result {
  /**
   * @template T
   * @param {T} value
   * @returns {Success<T>}
   */
  static success(value) {
    return new Success(value);
  }

  /**
   * @template E
   * @param {E} error
   * @returns {Failure<E>}
   */
  static failure(error) {
    return new Failure(error);
  }
}

const res = Math.random() > 0.5 ? Result.success(42) : Result.failure("error");

if (res.isOk()) {
  console.log(res.value());
  console.log(res.error());
} else {
  console.log(res.value());
  console.log(res.error());
}
```

## Summary

Type Narrowing can be used to narrow down a type from a union type to a specific type. This allows the IDE to know which type you are working with and give errors or warnings about incorrect usage allowing you to write safer code.

[Follow me on X](https://x.com/joelnet)

Cheers 🍻
