You can do something like this (pseudocode-ish; link to buildable code is provided below):

// wrap std::optional for chaining
template <class T> class Maybe {
  std::optional<T> t;

  // ... constructors etc

  // Maybe chaining 
  // If A has a member named m of type M, 
  // then Maybe<A>.fetch(&A::m) returns a Maybe<M>

  template <class M>
  Maybe<M> fetch(M T::*mem_ptr) {
     return (bool(t)) ? Maybe<M>((*t).*mem_ptr) : Maybe<M>() ;
  }

  // Maybe chaining special case 
  // If A has a member named m, which is itself a Maybe<M>,
  // then return it without wrapping it in an additional Maybe

  template <class M>
  Maybe<M> fetch(Maybe<M> T::*mem_ptr) {
     return (bool(t)) ? ((*t).*mem_ptr) : Maybe<M>() ;
  }

};

Now if you have this:

 struct C { int d ; }
 struct B { C c; }
 struct A { B b; }
 A a;
 Maybe<A> ma;

and you can do this

 int d = a.b.c.d;

you cannot do the same with ma, but you can use the next best thing, namely:

 Maybe<int> md = ma.fetch(&A::b).fetch(&B::c).fetch(&C::d);

And you can still use this if you Maybe-ify any or all struct members above:

 struct C { Maybe<int> d ; }
 struct B { Maybe<C> c; }
 struct A { Maybe<B> b; }

Live example (not production quality but it builds).

Answer from n. m. could be an AI on Stack Overflow
Top answer
1 of 6
4

You can do something like this (pseudocode-ish; link to buildable code is provided below):

// wrap std::optional for chaining
template <class T> class Maybe {
  std::optional<T> t;

  // ... constructors etc

  // Maybe chaining 
  // If A has a member named m of type M, 
  // then Maybe<A>.fetch(&A::m) returns a Maybe<M>

  template <class M>
  Maybe<M> fetch(M T::*mem_ptr) {
     return (bool(t)) ? Maybe<M>((*t).*mem_ptr) : Maybe<M>() ;
  }

  // Maybe chaining special case 
  // If A has a member named m, which is itself a Maybe<M>,
  // then return it without wrapping it in an additional Maybe

  template <class M>
  Maybe<M> fetch(Maybe<M> T::*mem_ptr) {
     return (bool(t)) ? ((*t).*mem_ptr) : Maybe<M>() ;
  }

};

Now if you have this:

 struct C { int d ; }
 struct B { C c; }
 struct A { B b; }
 A a;
 Maybe<A> ma;

and you can do this

 int d = a.b.c.d;

you cannot do the same with ma, but you can use the next best thing, namely:

 Maybe<int> md = ma.fetch(&A::b).fetch(&B::c).fetch(&C::d);

And you can still use this if you Maybe-ify any or all struct members above:

 struct C { Maybe<int> d ; }
 struct B { Maybe<C> c; }
 struct A { Maybe<B> b; }

Live example (not production quality but it builds).

2 of 6
3

C++23 introduces and_then or or_else to address this inconvenience.

Here is some paper with proposal.

Before we can use C++23 you can try write some template which could resolve this.

My attempt:

namespace detail {
template <auto Field, class T>
struct field_from_opt;

template<typename T, typename FieldType, FieldType T::*ptr>
struct field_from_opt<ptr, T>
{
    static auto get(const std::optional<T>& x) -> std::optional<FieldType>
    {
        if (x) return (*x).*ptr;
        return {};
    }
};
}

template<auto Field, typename T>
auto if_exists(const std::optional<T>& x)
{
    return detail::field_from_opt<Field, T>::get(x);
}

https://godbolt.org/z/dscjYqrx1

🌐
GitHub
github.com › tc39 › proposal-optional-chaining-assignment
GitHub - tc39/proposal-optional-chaining-assignment: `a?.b = c` proposal
Proposal to add support for optional chaining on the left of assignment operators: a?.b = c.
Starred by 199 users
Forked by 3 users
🌐
GitHub
github.com › tc39 › proposal-optional-chaining
GitHub - tc39/proposal-optional-chaining
An Optional Chain may be followed by another Optional Chain. a?.b[3].c?.(x).d a == null ? undefined : a.b[3].c == null ?
Starred by 4.9K users
Forked by 72 users
Languages   HTML 97.2% | Shell 2.8%
🌐
TC39
tc39.es › proposal-optional-chaining
Optional Chaining
An early version of this proposal used a Nil reference to express short-circuiting. This one is based on syntax only. ... Optional chaining is forbidden in write contexts such as a?.b = c.
🌐
Wikipedia
en.wikipedia.org › wiki › Safe_navigation_operator
Safe navigation operator - Wikipedia
1 month ago - In object-oriented programming, ... operator, null-propagation operator) is a binary operator that returns null if its first argument is null; otherwise it performs a dereferencing operation as specified by the second argument (typically an object member access, array index, ...
🌐
DEV Community
dev.to › eljayadobe › comment › 1pog7
When I've asked about optional chaining in C++, the push back I've gotten has... - DEV Community
Compiled languages: C++, Swift, C#, and F#. Script languages: Python and Lua. Shell language: bash. Platforms: macOS, iOS, Windows. ... Software engineer at Adobe on Photoshop. ... When I've asked about optional chaining in C++, the push back I've gotten has been that it's a code smell because optional chaining means high coupling and intimate knowledge of the private parts.
Find elsewhere
🌐
GeeksforGeeks
geeksforgeeks.org › javascript › javascript-optional-chaining
JavaScript Optional Chaining - GeeksforGeeks
... When we want to check a value ... Optional Chaining Operator allows a developer to handle many of those cases without repeating themselves by assigning intermediate results in temporary variables:...
Published   June 22, 2020
Top answer
1 of 6
11

C++23 introduced Monadic operations for optional, so you can use std::optional::transform

#include <optional>

using Bar = int;
struct Foo { Bar x; };

std::optional<Bar> f()
{
    std::optional<Foo> obj {};
    return obj.transform([](auto&& o) { return o.x;});
}

for lower C++ versions you could use C++11 optional library which has a similar operation (map), or write your own version of transform (not recommended).

godbolt demo

I don't recommend you write you own transform because to make it chainable you need to inherit from std::optional, and it is likely UB to inherit from the standard library objects.


A shorter version would define the lambda as a struct externally, if you are using it everywhere and need to reduce the number of letters per use. (with the exact same assembly)

template <typename Member>
struct grab
{
    Member member;
    auto operator()(auto& obj) const 
    { return obj.*member; }
};

std::optional<Bar> f()
{
    std::optional<Foo> obj {};
    return obj.transform(grab{&Foo::x});
}
2 of 6
5

I was surprised to discover C++ option lacked a map method, but after a few minutes thought, it seems easy to emulate.

template<class I, class O>
std::optional<O> map(const std::optional<I>& val, O const I::*member)
{ return val ? std::optional<O>{*val.*member} : std::nullopt; }

template<class I, class O, class...Args>
std::optional<O> map(const std::optional<I>& val, O (I::*method)() const, Args&&...args)
{ return val ? std::optional<O>{(*val.*method)(std::forward<Args>(args)...)} : std::nullopt; }

And then usage is terse:

map(obj, &Foo::x);
map(obj, &Foo::getX); //if you need to pass parameters to this, you can

This only works on members and methods, and not arbitrary function calls, but arbitrary functinon calls would require lambdas, which are annoyingly verbose in C++.

🌐
Geo-ant
geo-ant.github.io › blog › 2020 › null-conditional-operator-for-optionals
A null-conditional chaining operator for std::optional inspired by C#
In C++, std::optional<T> is a great way to represent a type that could hold a value of type T or nothing. However, it is somewhat clumsy to work with optional types when you want to chain operations on them because you have to account for the nullopt case.
🌐
Svbtle
nomothetis.svbtle.com › understanding-optional-chaining
Understanding Optional Chaining • Alexandros Salazar
Optionals in Swift are an interesting beast. On the one hand, they’re absolutely essential to dealing with Objective-C and C methods, which can return nil and NULL at will. On the other hand, they’re actually a pretty advanced concept that is... | Alexandros Salazar | One-man melting pot.
🌐
freeCodeCamp
forum.freecodecamp.org › javascript
What Is the Optional Chaining Operator, and How Does It Work?
April 8, 2025 - TLDR: Optional Chaining Operator is for objects, not properties? https://www.w3schools.com/jS/js_2020.asp " The Optional Chaining Operator returns undefined if an object is undefined or null (instead of throwing an er…
Top answer
1 of 4
13

I agree that these tags should be synonymised. There is also a null-propagation-operator tag which should be included in this.

I think safe-navigation-operator shouldn't be the canonical one. It has the fewest tagged questions, and it doesn't include null in its name. I also think "navigation" could be misleading to people who aren't already familiar with this usage of it (normally I would say property access, array access, etc., not property navigation or array navigation). I would favour null-conditional-operator or null-propagation-operator being the canonical one, since the word null appearing in the tag name makes the tag more useful when it appears on the main page or in searches.

2 of 4
7

I am admittedly biased as I encounter the null-conditional-operator on nearly a daily basis, and if I had a question about it, that's the only term I'd recognize of the three proposed.

I'll repeat a comment by Heretic Monkey to the OP here:

"optional chaining" in JavaScript is slightly different from "null conditional operator" in C# due to the presence of undefined in JavaScript. Optional chaining takes into account both null and undefined, whereas C#'s null conditional operator does not have to. I don't know if that's enough of a difference to keep them separate (there may be questions unique to JS's handling of undefined), but I thought I'd throw it out there.

To me, this is sufficient differentiation not to combine at these two tags. They are specifically documented features of major programming languages, and have differences in their implementations.

I won't speak to the more vague "safe navigation" operator and wouldn't oppose combining it with something or making it angular-specific, but I see more value in the separate tags than I would with a combined/synonomized tag.

Top answer
1 of 5
589

You need to put a . after the ? to use optional chaining:

myArray.filter(x => x.testKey === myTestKey)?.[0]

Playground link

Using just the ? alone makes the compiler think you're trying to use the conditional operator (and then it throws an error since it doesn't see a : later)

Optional chaining isn't just a TypeScript thing - it is a finished proposal in plain JavaScript too.

It can be used with bracket notation like above, but it can also be used with dot notation property access:

const obj = {
  prop2: {
    nested2: 'val2'
  }
};

console.log(
  obj.prop1?.nested1,
  obj.prop2?.nested2
);
Run code snippetEdit code snippet Hide Results Copy to answer Expand

And with function calls:

const obj = {
  fn2: () => console.log('fn2 running')
};

obj.fn1?.();
obj.fn2?.();
Run code snippetEdit code snippet Hide Results Copy to answer Expand

2 of 5
50

Just found it after a little searching on the what's new page on official documentation

The right way to do it with array is to add . after ?

so it'll be like

myArray.filter(x => x.testKey === myTestKey)?.[0] // in case of object
x?.() // in case of function

I'll like to throw some more light on what exactly happens with my above question case.

myArray.filter(x => x.testKey === myTestKey)?[0]

Transpiles to

const result = myArray.filter(x => x.testKey === myTestKey) ? [0] : ;

Due to which it throws the error since there's something missing after : and you probably don't want your code to be transpilled to this.

Thanks to Certain Performance's answer I learned new things about typescript especially the tool https://www.typescriptlang.org/play/index.html .

🌐
Keegan
keegan.codes › blog › the-downside-of-optional-chaining-overuse
The Dangers of Optional Chaining Overuse
All your application knows is that result is undefined, but it can't properly log an error or display a message about what the issue is, since that information has been lost. Optional chaining is slower than traditional property access because there are additional checks involved.
🌐
Swift Forums
forums.swift.org › using swift
How does the optional chaining work here - Using Swift - Swift Forums
February 5, 2024 - I do not seem to understand how optional chaining works. The first print works as I expect. The second works, but how and why? The third one fails, but I thought I was accessing .count on Optional("") because that is what I get when I do print((s as? SomethingElse)?.some_string) class Something {} class SomethingElse: Something {var some_string: String = ""} let s: Something = SomethingElse() // explicit parenthesis - works print(((s as?
🌐
MDN Web Docs
developer.mozilla.org › en-US › docs › Web › JavaScript › Reference › Operators › Optional_chaining
Optional chaining (?.) - JavaScript | MDN
This is an idiomatic pattern in JavaScript, but it gets verbose when the chain is long, and it's not safe. For example, if obj.first is a Falsy value that's not null or undefined, such as 0, it would still short-circuit and make nestedProp become 0, which may not be desirable. With the optional chaining operator (?.), however, you don't have to explicitly test and short-circuit based on the state of obj.first before trying to access obj.first.second:
🌐
Geo-ant
geo-ant.github.io › blog › 2020 › optional-pipe-syntax-part-1-fundamentals
Implementing a Pipe Syntax for std::optional - Part 1: Fundamentals
If the given optional is not empty, the callable is applied to the value inside the argument and an optional containing the result is returned. So the signature of the operator is operator|: const std::optional<T> &, F&&\(\rightarrow\)std::optional<U>, where U is the return type of the callable. This operator is already quite neat to use because it can be chained.