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.
Every Crazy Thing JavaScript Does
  A captivating guide to the subtle caveats and lesser-known parts of JavaScript.
 

