Question
Optional Chaining in TypeScript: Does TypeScript Support `?.` and What Is It Called?
Question
In TypeScript, is the ?. operator supported for safe property access?
For example:
const thing = foo?.bar;
Is this equivalent to something like:
const thing = foo ? foo.bar : null;
Also, what is the standard or most commonly used name for this operator? It can be difficult to search for because the symbol is so short.
Short Answer
By the end of this page, you will understand that TypeScript does support ?., the operator is commonly called optional chaining, and it is used to safely access properties, call methods, or read array elements when a value might be null or undefined. You will also see how it differs from a normal ternary check and how developers use it in real TypeScript code.
Concept
TypeScript supports the ?. operator, and its common name is optional chaining.
Optional chaining lets you safely continue an expression only if the value on the left is not null or undefined. If the value is null or undefined, the whole expression stops and returns undefined instead of throwing an error.
For example:
const city = user?.address?.city;
This means:
- If
userexists, try to readaddress - If
addressexists, try to readcity - If either
useroraddressisnullorundefined, returnundefined
This matters because real programs often deal with missing data:
- API responses may omit fields
- Optional configuration values may not exist
- Callback functions may or may not be provided
- Nested objects may be partially loaded
Without optional chaining, code becomes repetitive:
const city = user && user.address && user.address.city;
Or more explicit:
const city = user ? (user.address ? user.address.city : undefined) : undefined;
Optional chaining makes this much cleaner.
One important detail: foo?.bar is not exactly the same as:
foo ? foo.bar : null
Differences:
foo?.barchecks specifically fornullorundefinedfoo ? foo.bar : nullchecks for all falsy values, such as0,"", andfalsefoo?.barreturnsundefinedwhen access stops, notnull
So optional chaining is more precise and usually safer than a truthiness check.
Mental Model
Think of optional chaining like walking through a building with automatic doors.
- If the next door exists and opens, you keep going
- If you reach a missing or locked door (
nullorundefined), you stop safely - Instead of crashing into the wall with an error, you simply end up with
undefined
Example:
const result = company?.department?.manager?.name;
You are trying to walk through:
companydepartmentmanagername
If any step in that path is missing, the process stops there safely.
Without optional chaining, you would have to manually check every door before opening the next one.
Syntax and Examples
Basic syntax
object?.property
object?.[index]
object?.method()
Optional chaining can be used in three common ways:
1. Safe property access
type User = {
name: string;
profile?: {
bio?: string;
};
};
const user: User = {
name: "Ava"
};
const bio = user.profile?.bio;
console.log(bio); // undefined
Here, profile is optional. If it does not exist, user.profile?.bio safely returns undefined.
2. Safe array access
const items = ["a", "b", "c"];
const first = items?.[0];
console.log(first); // "a"
This is especially useful when the array itself might be missing.
const items2: string[] | undefined = undefined;
const first2 = items2?.[0];
console.log(first2); // undefined
3. Safe method call
type Logger = {
log?: (message: string) => void;
};
const logger: Logger = {};
logger.log?.("Hello"); // No error
If log exists, it is called. If not, nothing happens and the expression returns undefined.
Comparing with a ternary
const thing = foo?.bar;
This is closer to:
const thing = foo == null ? undefined : foo.bar;
It is not the same as:
const thing = foo ? foo.bar : null;
Because that version treats 0, false, and "" as missing, which optional chaining does not.
Step by Step Execution
Consider this example:
type ApiResponse = {
user?: {
contact?: {
email?: string;
};
};
};
const response: ApiResponse = {
user: {
contact: {
email: "ava@example.com"
}
}
};
const email = response.user?.contact?.email;
console.log(email);
Step by step:
-
response.useris checked- It exists, so evaluation continues
-
response.user.contactis checked- It exists, so evaluation continues
-
response.user.contact.emailis read- It exists and equals
"ava@example.com"
- It exists and equals
-
emailgets the value"ava@example.com"
Now change the data:
const response2: ApiResponse = {};
const email2 = response2.user?.contact?.email;
console.log(email2);
Step by step:
-
response2.useris checked- It is
undefined
- It is
-
Because optional chaining is used, evaluation stops immediately
-
contactandemailare not accessed -
email2becomesundefined
This prevents a runtime error like:
Cannot read properties of undefined
Real World Use Cases
Optional chaining appears often in real applications where data may be incomplete.
API responses
const avatarUrl = response.data?.user?.profile?.avatarUrl;
APIs often return partial or optional fields.
Form validation
const errorMessage = errors.email?.message;
Validation libraries often store errors only for fields that failed.
Optional callbacks
props.onSave?.();
A component may receive a callback prop, but it may not always be provided.
Configuration objects
const timeout = config.network?.timeout;
Different environments may define different config sections.
Browser or platform features
const language = navigator.languages?.[0];
Some platform APIs may not exist in every runtime.
Database or nested domain models
const managerName = employee.department?.manager?.name;
Large object graphs often contain optional relationships.
Real Codebase Usage
In real TypeScript projects, optional chaining is commonly used with a few important patterns.
Reading nested data safely
Developers often consume JSON from APIs where many fields are optional.
const zipCode = order.customer?.address?.zipCode;
Optional function execution
This is a common pattern for event handlers, hooks, and callbacks.
options.onComplete?.(result);
Pairing with nullish coalescing
Optional chaining is often combined with ?? to provide a default value.
const theme = settings.userPreferences?.theme ?? "light";
This means:
- Try to read
theme - If the result is
nullorundefined, use"light"
Guarding without deep nesting
Instead of many if statements:
if (request.user && request.user.session && request.user.session.id) {
// use id
}
Developers often write:
const sessionId = request.user?.session?.id;
if (sessionId) {
// use id
}
Cleaner UI rendering
In frontend code:
const displayName = user.profile?.displayName ?? user.name;
This keeps rendering logic concise while still being safe.
Logging and error handling
console.error(error.response?.data?.message ?? "Unknown error");
This avoids crashes while extracting useful information from uncertain objects.
Common Mistakes
1. Treating ?. as a general falsy check
Broken assumption:
const result = value?.name;
Some beginners think this stops when value is 0, false, or "". It does not. It only stops for:
nullundefined
2. Assuming it returns null
const name = user?.profile?.name;
If the chain stops, the result is undefined, not null.
If you need a fallback:
const name = user?.profile?.name ?? "Anonymous";
3. Using it where the left side cannot be nullish
const user = { name: "Ava" };
const name = user?.name;
This is valid, but unnecessary if user is guaranteed to exist.
4. Confusing ?. with &&
Old pattern:
const name = user && user.profile && user.profile.name;
Better modern pattern:
const name = user?.profile?.name;
But remember: && uses truthiness, while ?. uses nullish checks.
5. Forgetting that only one step is protected
Broken code:
const value = user?.profile.name;
If user exists but profile is undefined, this still throws.
Safer version:
const value = user?.profile?.name;
6. Expecting optional chaining to fix all invalid operations
Broken code:
const result = maybeNumber?.toFixed(2);
This is only safe if maybeNumber may be null or undefined. It does not protect you from wrong types.
For example, if maybeNumber is a string, you still have a type problem.
7. Overusing it instead of validating important data
Optional chaining is useful, but if a value must exist for your program to work, validate it clearly instead of silently continuing.
if (!config.apiKey) {
throw new Error("Missing API key");
}
Comparisons
| Concept | What it checks | Result when missing | Notes |
|---|---|---|---|
foo?.bar | foo is null or undefined | undefined | Precise nullish-safe access |
foo ? foo.bar : null | foo is truthy | null | Treats 0, false, "" as missing |
foo && foo.bar | foo is truthy | returns foo if falsy | Older JavaScript pattern |
foo!.bar | No runtime check | accesses directly | Non-null assertion; tells TypeScript to trust you |
Optional chaining vs non-null assertion
const name1 = user?.profile?.name;
const name2 = user!.profile.name;
?.is safe at runtime!is not a runtime safety feature!only tells the TypeScript compiler to stop warning you
Optional chaining vs nullish coalescing
const name = user?.profile?.name ?? "Anonymous";
?.safely reads a value??provides a fallback if that value isnullorundefined
They are often used together.
Cheat Sheet
// Property access
obj?.prop
// Array or index access
obj?.[index]
// Method call
obj?.method()
Key rules
?.is called optional chaining- It stops only for
nullorundefined - If it stops, the result is
undefined - It can be used for properties, indexes, and method calls
- It does not treat
0,false, or""as missing
Good examples
const city = user?.address?.city;
const firstItem = list?.[0];
callback?.("done");
Common pairing
const city = user?.address?.city ?? "Unknown";
Rough equivalent
const city = user == null ? undefined : user.address;
Not equivalent to
const city = user ? user.address : null;
Because that uses truthiness, not nullish checks.
FAQ
What is the ?. operator called in TypeScript?
It is called optional chaining.
Does TypeScript support optional chaining?
Yes. TypeScript supports optional chaining and uses the same ?. syntax as modern JavaScript.
Is foo?.bar the same as foo ? foo.bar : null?
No. Optional chaining checks only for null and undefined, and it returns undefined if access stops.
What does optional chaining return if something is missing?
It returns undefined.
Can I use optional chaining with methods?
Yes.
logger?.log?.("Hello");
Can I use optional chaining with arrays?
Yes.
const first = items?.[0];
Should I always use optional chaining?
No. Use it when a value may legitimately be missing. If a value is required, validate it explicitly.
What should I search for to learn more about ?.?
Search for TypeScript optional chaining or JavaScript optional chaining.
Mini Project
Description
Build a small TypeScript utility that reads profile information from possibly incomplete user data. This demonstrates how optional chaining helps when working with API-style objects where some nested fields may be missing.
Goal
Create a function that safely reads nested user information and returns sensible fallback values.
Requirements
- Define a TypeScript type for a user with optional nested profile and contact fields.
- Create at least two example users, including one with missing nested data.
- Write a function that returns the user's display name and email safely.
- Use optional chaining to access nested properties.
- Use nullish coalescing to provide default values when data is missing.
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.