Optional Chaining in JavaScript: Methods & Indexing

Optional chaining (?.) in JavaScript provides a safe way to access object properties, execute methods, and look up dynamic keys or array elements without having to manually verify that each reference in the chain is valid. When an evaluation encounters a null or undefined value, the expression short-circuits and evaluates to undefined instead of throwing a TypeError. This article explains how the optional chaining operator functions when applied specifically to method invocations and dynamic property indexing.

Optional Method Invocations

Optional chaining can be used to call a method that may or may not exist on an object using the ?.() syntax.

Syntax and Basic Usage

When calling a method that might not be defined, place ?. before the parentheses:

const user = {
  name: "Alex",
  getRole: () => "Admin"
};

// Calling an existing method
console.log(user.getRole?.()); // Output: "Admin"

// Calling a non-existent method
console.log(user.getPermissions?.()); // Output: undefined

If the object itself might be null or undefined, chain the operator before the method name as well:

const guest = null;

// Safe call on a potentially nullish object
console.log(guest?.getRole?.()); // Output: undefined

Important Caveat: Non-Function Types

Optional chaining only checks if the value before ?.() is null or undefined. If the property exists but contains a value that is not a callable function (such as a string, number, or object), JavaScript will still throw a TypeError.

const config = {
  customAction: "not a function"
};

// Throws TypeError: config.customAction is not a function
config.customAction?.();

Optional Property Indexing (Bracket Notation)

Optional property indexing allows safe access to dynamic object properties and array elements using the ?.[] syntax.

Dynamic Object Keys

When looking up object properties using variables or computed keys, place ?. before the opening bracket:

const themeSettings = {
  dark: { background: "#000" }
};

const userPreference = "light";

// Accessing a key that does not exist
console.log(themeSettings?.[userPreference]?.background); // Output: undefined

Array Element Access

The ?.[] notation works the same way for arrays, guarding against indexing into null or undefined collections:

function getFirstItem(list) {
  return list?.[0];
}

console.log(getFirstItem(["apple", "banana"])); // Output: "apple"
console.log(getFirstItem(null));               // Output: undefined
console.log(getFirstItem(undefined));          // Output: undefined

Short-Circuit Evaluation

Optional chaining relies on short-circuiting. If the target on the left-hand side of ?. evaluates to null or undefined, any subsequent expressions, method arguments, or nested property lookups on that chain are not executed.

let counter = 0;
const increment = () => ++counter;

const handler = null;

// increment() is never called because handler is null
handler?.[increment()]();

console.log(counter); // Output: 0

Combining Indexing and Method Invocations

You can combine both techniques to safely retrieve a function by dynamic key and execute it in a single statement:

const actions = {
  save: () => "Saved successfully!"
};

const actionName = "delete";

// Safely attempts to find and invoke the dynamic method
const result = actions?.[actionName]?.();

console.log(result); // Output: undefined

By using ?.() for method calls and ?.[] for computed lookups, you can write concise, defensive JavaScript code that avoids runtime TypeError: Cannot read properties of undefined exceptions.