This new JavaScript operator is an absolute game changer

Last updated on September 17, 2024
This new JavaScript operator is an absolute game changer

With the new safe assignment ?= operator you'll stop writing code like this:

// ❌ Before:

// ❌ Deep nesting of try-catch for different errors

async function fetchData() {
  try {
    const response = await fetch('https://api.codingbeautydev.com/docs');
    try {
      const data = await response.json();
      return data;
    } catch (parseError) {
      console.error(parseError);
    }
  } catch (networkError) {
    console.error(networkError);
  }
}

And start writing code like this:

// ✅ After:

async function fetchData() {
  const [networkError, response] ?= await fetch('https://codingbeautydev.com');
  
  if (networkError) return console.error(networkError);
  
  const [parseError, data] ?= await response.json();
  
  if (parseError) return console.error(parseError);
  
  return data;
}

We've completely eradicated the deep nesting. The code is far more readable and cleaner.

Instead of getting the error in the clunky catch block:

async function doStuff() {
  try {
    const data = await func('codingbeautydev.com');
  } catch (error) {
    console.error(error);
  }
}

Now we do everything in just one line.

Instead of failing loudly and proudly, ?= tells the error to shut up and let us decide what do with it.

// ✅ if there's error: `err` has value, `data` is null
// ✅ no error: `err` is null, `data has value
async function doStuff() {
  const [err, data] ?= await func('codingbeautydev.com');
}

We can tell it to get lost:

async function doStuff() {
  // 👇 it's as good as gone here
  const [, data] ?= await func('codingbeautydev.com');
  // ...
}

We can announce it to the world and keep things moving:

async function doStuff() {
  const [err, data] ?= await func('codingbeautydev.com');
  if (err) {
    console.error(err);
  }
  // ...
}

Or we can stop immediately:

// `err` is null if there's no error
async function doStuff() {
  const [err, data] ?= await func('codingbeautydev.com');
  if (err) return;
}

Which makes it such a powerful tool for creating guard clauses:

// ✅ avoid nested try-catch
// ✅ avoid nested ifs
function processFile() {
  const filename = 'codingbeautydev.com.txt';

  const [err, jsonStr] ?= fs.readFileSync(filename, 'utf-8');
  if (readErr) {
    return;
  }

  const [jsonErr, json] ?= JSON.parse(jsonStr);
  if (jsonErr) {
    return;
  }

  const awards = json.awards.length;
  console.log(`🏅Total awards: ${awards}`);
}

And here's one of the very best things about this new operator.

There are several instances where we want a value that depends on whether or not there's an exception.

Normally you'll use a mutable var outside the scope for error-free access:

function writeTransactionsToFile(transactions) {
  // ❌ mutable var
  let writeStatus;

  try {
    fs.writeFileSync(
      'codingbeautydev.com.txt',
      transactions
    );
    writeStatus = 'success';
  } catch (error) {
    writeStatus = 'error';
  }
  // do something with writeStatus...
}

But this can be frustrating, especially when you're trying to have immutable code and the var was already const before the time came to add try-catch.

You'd have to wrap it try, then remove the const, then make a let declaration outside the try, then re-assign again in the catch...

But now with ?=:

function writeTransactionsToFile(transactions) {
  const [err, data] ?= fs.writeFileSync(
    'codingbeautydev.com.txt',
    transactions
  )
  const writeStatus = err ? 'error' : 'success'
  // // do something with writeStatus...
}

We maintain our immutability and the code is now much more intuitive. Once again we've eradicated all nesting.

How does it work?

The new ?= operator calls the Symbol.result method internally.

So when we do this:

const [err, result] ?= func('codingbeautydev.com');

This is what's actually happening:

// it's an assignment now
const [err, result] = func[Symbol.result](
  'codingbeautydev.com'
);

So you know what this means right?

It means we can make this work with ANY object that implements Symbol.result:

function doStuff() {
  return {
    [Symbol.result]() {
      return [new Error("Nope"), null];
    },
  };
}

const [error, result] ?= doStuff();

But of course you can throw as always:

function doStuff() {
  throw new Error('Nope');
}

const [error, result] ?= doStuff();

And one cool thing it does: if result has its own Symbol.result method, then ?= drills down recursively:

function doStuff() {
  return {
    [Symbol.result](str) {
      console.log(str);
      return [
        null,
        {
          [Symbol.result]() {
            return [new Error('WTH happened?'), null];
          }
        }
      ];
    }
  }
}

const [error, result] ?= doStuff('codingbeautydev.com');

You can also use the object directly instead of returning from a function:

const obj = {
  [Symbol.result]() {
    return [new Error('Nope'), null];
  },
};

const [error, result] ?= obj;

Although, where would this make any sense in practice?

As we saw earlier, ?= is versatile enough to fit in seamlessly with both normal and awaited functions.

const [error, data] ?= fs.readFileSync('file.txt', 'utf8');

const [error, data] ?= await fs.readFile('file.txt', 'utf8');

using

?= also works with the using keyboard to automatically clean up resources after use.

❌ Before:

try {
  using resource = getResource();
} catch (err) {
  // ...
}

✅ After:

using [err, resource] ?= getResource();

How to use it now

While we wait for ?= to become natively integrated into JavaScript, we can start it now with this polyfill:

But you can't use it directly -- you'll need Symbol.result:

import 'polyfill.js';

const [error, data] = fs
  .readFileSync('codingbeautydev.com.txt', 'utf-8')
  [Symbol.result]();

Final thoughts

JavaScript error handling just got much more readable and intuitive with the new safe assignment operator (?=).

Use it to write cleaner and more predictable code.

Coding Beauty Assistant logo

Try Coding Beauty AI Assistant for VS Code

Meet the new intelligent assistant: tailored to optimize your work efficiency with lightning-fast code completions, intuitive AI chat + web search, reliable human expert help, and more.

See also