# The Safe Assignment Operator (?=) in JavaScript — No More try-catch

# The Safe Assignment Operator (?=) in JavaScript — No More try-catch

Error handling in JavaScript has always been verbose. The `try-catch` pattern works, but it produces deeply nested code, makes control flow harder to follow, and scales poorly when multiple async operations need independent error handling.

The **Safe Assignment Operator (`?=`)** is a [TC39 proposal](https://github.com/arthurfiorette/proposal-safe-assignment-operator) that aims to fix this by bringing Go-style error handling to JavaScript.

## The Problem with try-catch

Consider a typical async operation:

```js
try {
  const response = await fetch('https://api.example.com/data');
  const data = await response.json();
  console.log(data);
} catch (err) {
  console.error('Something failed:', err);
}
```

This looks fine for one operation. But real-world code chains multiple async calls, each of which can fail independently:

```js
try {
  const response = await fetch('https://api.example.com/users');
  const users = await response.json();

  try {
    const response2 = await fetch(`https://api.example.com/users/${users[0].id}/posts`);
    const posts = await response2.json();

    try {
      await saveToDatabase(posts);
    } catch (err) {
      console.error('Database error:', err);
    }
  } catch (err) {
    console.error('Posts fetch error:', err);
  }
} catch (err) {
  console.error('Users fetch error:', err);
}
```

Three levels deep. Each `try-catch` adds indentation, obscures the happy path, and makes the code harder to maintain. This is sometimes called the **"try-catch pyramid of doom."**

## How ?= Works

The Safe Assignment Operator destructures the result of an expression into a tuple `[error, value]`:

```js
const [err, response] ?= await fetch('https://api.example.com/data');

if (err) {
  console.error('Fetch failed:', err);
  return;
}

const [parseErr, data] ?= await response.json();

if (parseErr) {
  console.error('Parse failed:', parseErr);
  return;
}

console.log(data);
```

No nesting. No indentation creep. Each operation handles its own error inline. The control flow reads top to bottom — exactly how the code executes.

## Why This Matters

### Flat Error Handling

The `?=` operator eliminates nesting. Every async call returns a consistent `[error, result]` tuple, and you handle it immediately. The happy path stays at the top level.

### Explicit Error Granularity

With `try-catch`, a single block catches errors from multiple statements. You often don't know *which* operation failed without additional logic. With `?=`, each operation has its own error variable.

### Cleaner Async Pipelines

When chaining multiple operations that can each fail:

```js
const [fetchErr, response] ?= await fetch(url);
if (fetchErr) return handleError(fetchErr);

const [parseErr, data] ?= await response.json();
if (parseErr) return handleError(parseErr);

const [saveErr] ?= await db.save(data);
if (saveErr) return handleError(saveErr);

return { success: true, data };
```

This pattern scales linearly. Ten operations? Ten flat checks. No pyramid.

## Inspiration from Go

If this looks familiar, it's because Go has used this pattern since its inception:

```go
data, err := ioutil.ReadFile("file.txt")
if err != nil {
    log.Fatal(err)
}
```

Go's approach has been praised for making error handling explicit and impossible to accidentally ignore. The `?=` operator brings the same discipline to JavaScript.

## The Pattern You Can Use Today

While `?=` is still a proposal (not yet part of the ECMAScript standard), you can achieve the same pattern with a simple utility function:

```js
async function safe(promise) {
  try {
    const result = await promise;
    return [null, result];
  } catch (err) {
    return [err, null];
  }
}

// Usage
const [err, response] = await safe(fetch('https://api.example.com/data'));

if (err) {
  console.error(err);
  return;
}

console.log(response);
```

Five lines of code. No dependencies. Works everywhere today. When `?=` eventually ships in browsers, migrating is a one-line search-and-replace.

## Current Status

The Safe Assignment Operator is a **Stage 1 TC39 proposal** as of 2024. This means:

- It has a formal specification document.
- It's being actively discussed by the committee.
- It could change significantly before reaching Stage 3/4.
- No browser or runtime supports it natively yet.

Track the progress on [GitHub](https://github.com/arthurfiorette/proposal-safe-assignment-operator).

## The Bottom Line

Error handling should be simple, explicit, and flat. The `?=` operator doesn't add new capability — you can already handle errors with `try-catch`. But it changes the *ergonomics* of error handling in a way that makes code cleaner, more readable, and easier to maintain.

In the meantime, the `safe()` utility function gives you the same pattern today — zero dependencies, five lines, works everywhere.

**Better error handling isn't about catching more errors. It's about making error paths as readable as success paths.**

---

*By [estebanrfp](https://estebanrfp.com) — Full Stack Developer, dWEB R&D*

