Free JavaScript reference · Python timeline

JavaScript ES2020: Safer optional access and larger integers

Optional chaining, nullish defaults, BigInt, and allSettled make absence and asynchronous outcomes more explicit.

Edition 11 · Published · Editorial review October 26, 2026

Standardized in ES2020 · Syntax

Optional chaining with ?.

Optional chaining stops an access chain when the current receiver is null or undefined, producing undefined. It is useful for optional nested records and callbacks. It does not stop for every falsey value.

A chain does not prove a value has the right type. An optional call still fails if a present property is not callable. The root identifier must also exist; optional chaining is not a way to refer to an undeclared variable.

The example combines optional access with a separate defaulting decision. Keep chains continuous: grouping an intermediate result can end the short-circuiting behavior.

Worked example

Uses ES2020 features · Script

const profile = {contact: null};
console.log(profile.contact?.email ?? "not supplied");
const callback = undefined;
console.log(callback?.() === undefined);

Expected output

not supplied
true

Example checked with Chromium 151.0.7922.34, Node.js 24.19.0.

Common mistake

Optional chaining is not input validation, and it cannot be an assignment target. A present non-function still throws when called.

Try it yourself

Change contact to {email: ""}. Predict the result and explain why the empty value is preserved.

Read more: ES2020 specification · MDN explanation and compatibility

Standardized in ES2020 · Syntax

Nullish defaults with ??

?? selects its right operand only when the left value is null or undefined. This differs from ||, which also replaces zero, false, and empty strings. Choose according to the data model's definition of absence.

The right operand is evaluated only when needed. Use parentheses when combining ?? with && or ||; JavaScript rejects an unparenthesized mixture to avoid ambiguous-looking code.

The example preserves an explicit zero retry count while supplying a value for an absent setting. Defaults and validation remain separate: a negative number is not absent simply because it is invalid.

Worked example

Uses ES2020 features · Script

console.log(0 ?? 3);
console.log(0 || 3);
console.log(null ?? "fallback");
console.log(JSON.stringify("" ?? "fallback"));

Expected output

0
3
fallback
""

Example checked with Chromium 151.0.7922.34, Node.js 24.19.0.

Common mistake

Do not use ?? as a range or type check. It handles only null and undefined.

Try it yourself

Predict the results for false, NaN, and undefined with both || and ??.

Read more: ES2020 specification · MDN explanation and compatibility

Standardized in ES2020 · Built-in type

BigInt represents large integers

BigInt represents integers beyond Number's safe integer range. Add an n suffix to an integer literal or construct a value from an exact integer string. Arithmetic stays in the BigInt domain.

Do not first convert a large decimal string to Number and then to BigInt: precision might already have been lost. Number and BigInt arithmetic cannot be freely mixed, and division truncates toward zero.

BigInt is not a decimal-money type. Standard JSON.stringify also rejects it unless you deliberately define a serialization strategy.

Worked example

Uses ES2020 features · Script

const id = 9007199254740993n;
console.log(String(id + 2n));
console.log(String(7n / 2n));
try { JSON.stringify({id}); }
catch { console.log("choose a JSON representation"); }

Expected output

9007199254740995
3
choose a JSON representation

Example checked with Chromium 151.0.7922.34, Node.js 24.19.0.

Common mistake

Writing a large Number literal before converting to BigInt can preserve the already-rounded value rather than the intended integer.

Try it yourself

Construct the identifier from a string, then compare with BigInt(Number(theString)). Why can the results differ?

Read more: ES2020 specification · MDN explanation and compatibility

Standardized in ES2020 · Built-in async API

Promise.allSettled() keeps every outcome

allSettled waits for each supplied input to fulfill or reject, then returns an ordered list of outcome records. It is useful for independent work where one failure should not erase the useful results of other operations.

Result order matches input order rather than completion order. A fulfilled record has value; a rejected record has reason. Treat these shapes separately instead of assuming every item has a value.

This operation reports outcomes; it does not turn a failed operation into a successful one or decide whether the overall workflow should proceed.

Worked example

Uses ES2020 features · Script

const finished = Promise.allSettled([
  Promise.resolve(4),
  Promise.reject("missing")
]).then(results => {
  console.log(results.map(result => result.status).join(", "));
  console.log(results[0].value, results[1].reason);
});

Expected output

fulfilled, rejected
4 missing

Example checked with Chromium 151.0.7922.34, Node.js 24.19.0.

Common mistake

allSettled does not cancel work or limit how much is started. Handle failures deliberately after collecting the outcomes.

Try it yourself

Compare this example with Promise.all and predict which information reaches a success handler.

Read more: ES2020 specification · MDN explanation and compatibility

Recap

Optional chaining, nullish defaults, BigInt, and allSettled make absence and asynchronous outcomes more explicit. Check the engines your project supports before adopting new syntax or built-ins.

Read the ES2020 specification →