Question
JavaScript Optional Chaining with Arrays and Functions
Question
How can optional chaining be used with an array result and with a function property in JavaScript?
For example, how can I safely get the first matching item from an array?
myArray.filter(x => x.testKey === myTestKey)?[0]
I also want to conditionally call a function stored in an object property:
const x = { a: () => {}, b: null };
console.log(x?b());
Both attempts produce errors. What is the correct optional chaining syntax for array indexing and function calls?
Short Answer
Optional chaining uses ?. as one operator. In JavaScript, use ?.[index] to safely access an array element and ?.() to call a function only when the value is not null or undefined. You will also learn where to place optional chaining in a property, method, and array-access expression.
Concept
Optional chaining lets an expression stop safely when a value is null or undefined.
Instead of throwing an error such as:
const user = null;
console.log(user.name); // TypeError
use ?.:
const user = null;
console.log(user?.name); // undefined
The operator has forms for several kinds of access:
- Object property:
object?.property - Computed property or array index:
value?.[key]orvalue?.[0] - Function call:
fn?.()
The important detail is that ?. must stay together. Writing ?[0] is not optional chaining: ? begins the conditional (ternary) operator in JavaScript.
Optional chaining only handles null and undefined. It does not hide every possible error. For example, value?.() still throws if value exists but is not callable, such as a number or string.
Mental Model
Think of optional chaining as checking whether a path is still open before taking the next step.
user?.addressmeans: “If there is auser, look upaddress; otherwise stop and returnundefined.”items?.[0]means: “If there is anitemscollection, open drawer number0; otherwise stop.”callback?.()means: “If there is a callback function, press its button; otherwise do nothing and returnundefined.”
Each ?. protects the specific step immediately after it. Place it at the point where a value might be missing.
Syntax and Examples
Use these three optional chaining forms:
// Optional object property
object?.property;
// Optional computed property or array index
array?.[0];
object?.[dynamicKey];
// Optional function call
callback?.();
Optional array indexing
const colors = ["red", "blue"];
const noColors = null;
console.log(colors?.[0]); // "red"
console.log(noColors?.[0]); // undefined
The ?.[0] form checks whether colors exists before reading index 0.
Getting the first filtered result
filter() always returns an array when it is successfully called. Therefore, if myArray is definitely an array, no optional chaining is needed after filter():
const firstMatch = myArray.filter(item => item.testKey === myTestKey)[0];
If myArray itself might be null or undefined, protect the method call:
const firstMatch = myArray?.filter(item => item.testKey === myTestKey)?.[0];
The second ?. is useful because the expression might have stopped and returned undefined before indexing.
For finding one matching item, find() is usually clearer and avoids building a new filtered array:
const firstMatch = myArray?.find(item => item.testKey === myTestKey);
Optional function calls
const x = {
a: () => "ran a",
b: null
};
console.log(x.a?.()); // "ran a"
console.log(x.b?.()); // undefined
If the object may also be absent, protect both the object access and the call:
const maybeX = null;
console.log(maybeX?.a?.()); // undefined
Step by Step Execution
Consider this code:
const orders = [
{ id: 1, status: "pending" },
{ id: 2, status: "paid" }
];
const paidOrder = orders?.find(order => order.status === "paid");
console.log(paidOrder?.id);
Execution proceeds as follows:
orders?.find(...)checks whetherordersisnullorundefined.ordersis an array, so JavaScript callsfind().find()tests the first order. Its status is"pending", so it continues.- It tests the second order. Its status is
"paid", sofind()returns{ id: 2, status: "paid" }. paidOrder?.idchecks whether a matching order was returned.- It was, so the result is
2.
If no order had status "paid", find() would return undefined. Then paidOrder?.id would safely return undefined rather than throw an error.
For a callback example:
function saveRecord(record, onSaved) {
console.log(`Saving ${record.id}`);
onSaved?.(record);
}
saveRecord({ id: 7 }, null);
saveRecordlogs the record ID.onSavedisnull.onSaved?.(record)does not call anything and evaluates toundefined.- No error is thrown.
Real World Use Cases
Optional chaining is useful when data or behavior is genuinely optional.
-
API responses: An API may omit a nested field.
const city = response?.data?.customer?.address?.city; -
First item in optional data: A page may render before an API array arrives.
const avatarUrl = users?.[0]?.profile?.avatarUrl; -
Optional event handlers: A reusable component may accept a callback only in some situations.
onClose?.(); -
Optional integrations: An application may call a logger, analytics hook, or plugin only when configured.
analytics?.track?.("checkout_completed"); -
Searching fetched records: A matching record may not exist.
const email = customers?.find(c => c.id === customerId)?.email;
Real Codebase Usage
In real projects, optional chaining is commonly combined with clear defaults, validation, and early returns.
Optional callback pattern
Library and UI code often receives callbacks that callers may omit:
function downloadFile(url, onComplete, onError) {
try {
// Download work would happen here.
onComplete?.(url);
} catch (error) {
onError?.(error);
}
}
Guard clauses for required values
Do not use optional chaining when a value is required for the function to work. Validate it instead:
function sendWelcomeEmail(user) {
if (!user?.email) {
throw new Error("A user email is required.");
}
return `Email sent to ${user.email}`;
}
Default values with ??
Optional chaining returns undefined when it short-circuits. Use nullish coalescing to provide a fallback:
const firstTag = post?.tags?.[0] ?? "untagged";
?? only falls back for null and undefined, so valid values like 0, false, and "" are preserved.
Prefer find() for one result
When only one matching item is needed, codebases commonly use find() rather than filter()[0]:
const activeUser = users?.find(user => user.active);
It communicates the intent directly and stops searching after the first match.
Common Mistakes
Writing ?[0] instead of ?.[0]
This is invalid optional chaining syntax:
const first = items?[0];
Use:
const first = items?.[0];
? by itself belongs to a ternary expression such as condition ? yes : no.
Calling an object as though it were a function
This is not property access:
const x = { b: null };
x?b();
To access property b and call it only if it exists, use a dot and ?.():
x.b?.();
If x can also be missing:
x?.b?.();
Protecting the wrong part of the expression
This can still fail when users is null:
const name = users[0]?.name;
The array access happens before ?.name. Protect the array access itself:
const name = users?.[0]?.name;
Assuming optional calls validate function types
Optional calls skip only null and undefined:
const callback = 42;
callback?.(); // TypeError: callback is not a function
If a value may have an unknown type, check it explicitly:
if (typeof callback === "function") {
callback();
}
Using filter()[0] when find() expresses the intent
This works, but creates an array of all matches:
const user = users.filter(user => user.id === id)[0];
When only the first match matters, prefer:
const user = users.find(user => user.id === id);
Breaking a chain with parentheses
Optional chaining does not automatically protect operations after a grouped result:
const user = null;
const city = (user?.address).city; // TypeError
Continue the optional chain instead:
const city = user?.address?.city;
Comparisons
| Goal | Recommended syntax | Result when value is missing |
|---|---|---|
| Read an object property | user?.name | undefined |
| Read an array element | items?.[0] | undefined |
| Read a dynamic key | settings?.[key] | undefined |
| Call an optional function | onSave?.() | undefined; function is not called |
| Get the first matching array item | items?.find(predicate) | undefined |
| Get all matching array items | items?.filter(predicate) | undefined if items is missing; otherwise an array |
filter()[0] versus find()
| Expression | Returns | Best when |
|---|---|---|
items.filter(test)[0] | First item in a newly created matches array, or undefined | You also need the full list of matches elsewhere |
items.find(test) | First matching item, or undefined | You only need one match |
Optional chaining versus logical AND
Before optional chaining, developers often wrote:
const name = user && user.profile && user.profile.name;
The modern equivalent is clearer:
const name = user?.profile?.name;
Unlike &&, optional chaining specifically stops only for null and undefined. This makes its intent more precise.
Cheat Sheet
// Property
object?.property
// Computed property / array index
object?.[key]
array?.[0]
// Function call
fn?.()
object.method?.()
object?.method?.()
// Optional array method followed by an index
array?.filter(predicate)?.[0]
// Usually clearer for one match
array?.find(predicate)
// Fallback value
array?.[0] ?? "default value"
Rules:
- Write
?.together;?[0]is not valid optional chaining. - Use
?.[index]to protect an array access. - Use
?.()to protect a function call. object?.method()protectsobject, but not a missingmethod.object?.method?.()protects both the object and the method.- Optional chaining returns
undefinedwhen it encountersnullorundefined. - It does not prevent errors from calling a non-function value.
- Prefer
find()overfilter()[0]when you need only the first match.
FAQ
How do I use optional chaining to get the first array item?
Use array?.[0]. It returns the first item when the array exists and undefined when the array is null or undefined.
const first = products?.[0];
Why does ?[0] cause a JavaScript error?
Optional chaining requires a dot: ?.[0]. A standalone ? starts the conditional (ternary) operator, which requires both ? and : parts.
How do I optionally call a method on an object?
Use object.method?.() when the object exists but the method may be missing or null.
player.pause?.();
Use object?.method?.() when the object may also be missing.
Does array.filter(...)?[0] need optional chaining?
Not after a successful filter() call. filter() always returns an array, even when it is empty. Use array.filter(...)[0] if the array is guaranteed to exist. If it may not exist, use array?.filter(...)?.[0].
Should I use filter()[0] or find()?
Use find() when you need only the first matching item. It is clearer and does not create an array containing every match.
What does an optional function call return when it does not call anything?
It returns undefined.
const result = null?.();
console.log(result); // undefined
Does fn?.() prevent every function-call error?
No. It skips calls only when fn is null or undefined. If fn is 42, JavaScript still throws because 42 is not callable.
Can optional chaining replace input validation?
No. Use it for values that are legitimately optional. If a value is required, validate it and handle the error rather than silently continuing with undefined.
Mini Project
Description
Build a small order lookup helper that safely reads optional arrays and optionally notifies a caller when an order is found. This mirrors API data that may not be loaded yet and callback functions that may not be provided.
Goal
Find an order by ID without errors when the order list or callback is missing.
Requirements
Use optional chaining to handle a missing orders array.
Use find() to return one matching order.
Return a readable default message when no matching order exists.
Call an optional callback only when an order is found.
Safely read an optional customer name from the matched order.
Keep learning
Related questions
@Directive vs @Component in Angular: Differences, Use Cases, and When to Use Each
Learn the difference between @Directive and @Component in Angular, including use cases, examples, and when to choose each.
Accessing Input Value from EventTarget in TypeScript
Learn why EventTarget has no value property in TypeScript and safely read values from HTML input events in Angular applications.
Angular (change) vs (ngModelChange): What’s the Difference?
Learn the difference between Angular (change) and (ngModelChange), when each fires, and which one to use in forms and inputs.