---
description: '`obj ?. prop` syntax. 🚧 under construction -> SyntaxError: Unexpected token'
---

# optional chaining (?., ?.\[])

* [JS](../../../..) ⟩ [statement](../../../../grammar/statement) ⟩ [expression](../../../../grammar/statement/expr) ⟩ [operator](../../../../grammar/op) ⟩ [left-hand side](../../../../grammar/statement/expr/lhs) ⟩&#x20;
* [JS](../../../..) ⟩ [value](../../..) ⟩ [object](../..) ⟩ [property](..) ⟩ [access]() ⟩ optional chaining (?.)

{% hint style="success" %}
<mark style="color:yellow;">**(**</mark>[op](../../../../grammar/op "mention")<mark style="color:yellow;">**) (**</mark>[es2020](../../../../feature/es2020 "mention")<mark style="color:yellow;">**)**</mark> (:star2: [**chaining rules**](chaining-rules) <mark style="color:yellow;">**|**</mark> [**table of operators** ](../../../../grammar/op/table-of-operators))

<mark style="color:yellow;">**`obj`**</mark><mark style="color:purple;">**`?.`**</mark><mark style="color:yellow;">**`prop`**</mark> returns [**undefined**](../../../prim/undefined) <mark style="color:red;">**if**</mark> <mark style="color:yellow;">**`obj`**</mark> <mark style="color:yellow;">**is**</mark> [<mark style="color:red;">**nullish**</mark>](../../../prim/nullish).

```javascript
obj  ?. prop        // dot notation
obj  ?. [ expr ]    // bracket notation
func ?. ( args )    // conditional invocation
```

:u6307: <mark style="color:yellow;">**synonyms**</mark>**：**"<mark style="color:purple;">**conditional property access**</mark>", "<mark style="color:purple;">**optional chaining**</mark>"
{% endhint %}

{% tabs %}
{% tab title="🗺️" %}
<img src="https://2527454625-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2F-MfvEFZnSBhKT6fJmus0%2Fuploads%2FL7fbZTDQS5owq1y9IJlx%2Foptional.chaining.svg?alt=media&#x26;token=b9d8905f-6bb7-4e3e-b8e1-1214f6569787" alt="" class="gitbook-drawing">

* codepen：[optional chaining](https://codepen.io/lochiwei/pen/jOKERRa?editors=0012)

```javascript
// ⭐️ 1. prop: invalid identifier
// ------------------------------
undeclared ?. 1 = 'bad';  // ⛔ SyntaxError: Unexpected token
//            ^

// ⭐️ 2. obj: "undeclared"
// -------------------------------
undeclared ?. prop;       // ⛔ ReferenceError: 'undeclared' is not defined

// ⭐️ 3. obj: "nullish"
// -------------------------------
null ?. prop              // ❗ undefined

// ⭐️ 4. obj: not "nullish"
// -------------------------------
(18) ?. prop;            // ❗ undefined (no such `prop`)
(18) ?. toString(16);    // ❓ "12" (property value, could be any value)

// test object
let person = {
    
    name   : 'Tom',
    age    : 28,
    address: { street: 'Fifth Ave.', no: '23' },
    hobbies: ['TV', 'Game'],
    
    eat(food) { return 'poop' }
};
    
'------------ person ------------',

// ❗ "root" object (person) is NEVER protected

person ?. name,                 // "Tom"
//        ╰🛡️╯                
person ?. hobby,                // undefined
//        ╰🛡️─╯
person ?. address ?. street,    // "Fifth Ave."
//        ╰─🛡️──╯    ╰─🛡️─╯ 
person . address ?. district,   // undefined (✅ address: non-nullish)
//       ╰─❗──╯    ╰──🛡️──╯
person . hobby ?. [1],          // undefined (✅ hobby: nullish, protected)
//       ╰❗─╯   ╰🛡️╯ 
person . hobbies ?. [2],        // undefined (✅ hobbies: non-nullish)
//       ╰─❗──╯   ╰🛡️╯ 
person ?. eat ?. ('egg'),       // "poop"    (✅ eat: non-nullish)
//       ╰🛡️╯    ╰─🛡️──╯ 
person ?. make ?. (something),  // undefined (✅ make: direct prop, protected, short-circuiting)
//       ╰🛡️─╯    ╰───🛡️────╯


// jane
let jane = { 
    home     : null,
    boyfriend: { name: 'Joe' }
};

'------------ jane ------------',

// ❗ "root" object (jane) is NEVER protected

jane .  home,            // null (prop value)
//      ╰❗╯
jane ?. home,            // null (jane: non-nullish, = jane.home)
//      ╰🛡️╯ 

// 🛡️: "?." protect "nullish" obj and "direct" prop
// ❗: no protection, could be undeclared / nullish / error
// -----------------------------------------------------------
jane . home ?. c  ,          // undefined (✅ home: null, protected)
//     ╰❗╯   ╰🛡️╯ 
jane . home ?. ['hello'],    // undefined (✅ home: null, protected)
//     ╰❗╯   ╰───🛡️───╯ 

jane . home ?.  c .  d ,     // undefined (✅ home: null, short-circuiting)
//     ╰❗╯   ╰🛡️╯ ╰❗╯

// ╭─ UD ─╮         // UD: UnDefined
// (a.b?.c).d,      // ❌ DON'T DO THIS! (⛔ TypeError❗)
//       ^

jane . boyfriend ?. name,    // 'Joe' ( ✅ boyfriend: non-nullish)
//     ╰──❗───╯    ╰🛡️╯   
jane . boyfriend ?. money,   // undefine ( ✅ money: direct prop, protected)
//     ╰──❗───╯    ╰🛡️─╯   

jane . boyfriend ?. money . more,  // ⛔ TypeError (accessing `undefined.more`)
//     ╰──❗───╯    ╰🛡️─╯  ╰❗─╯      // (❌ more: not protected)

jane . boyfriend ?. money ?. more, // undefined ✅ (more: direct prop, protected)
//     ╰──❗───╯    ╰🛡️─╯   ╰🛡️─╯ 
```
{% endtab %}

{% tab title="🧨" %}
{% hint style="danger" %}
⭐️ the variable <mark style="color:yellow;">**before**</mark>**&#x20;**<mark style="color:purple;">**`?.`**</mark> <mark style="color:red;">**must**</mark>**&#x20;be&#x20;**<mark style="color:yellow;">**declared**</mark>❗️

```javascript
// ╭───❗️───╮  <----- ⭐️ "root" object is NEVER protected❗️
   undeclared?.address;
// ⛔ ReferenceError: `undeclared` is not defined
```
{% endhint %}

{% hint style="danger" %}
[jane.boyfriend-.money.more](chaining-rules/jane.boyfriend-.money.more "mention")&#x20;

<mark style="color:purple;">**`?.`**</mark> only protects nullish obj and direct prop:exclamation:

```javascript
// 🛡️ "?." only protects "nullish obj" and "direct prop"
// ❗ "."  provides NO protection.
// ❗ "root" object is NEVER protected.
jane . boyfriend ?. name          // ✅ boyfriend: non-nullish, ok.
//     ╰──❗───╯    ╰🛡️╯        
jane . boyfriend ?. money . more  // ✅ money: direct prop, protected.
//     ╰──❗───╯   ╰─🛡️─╯  ╰❗╯  // ❗ more: not protected, error may occur. 
```
{% endhint %}

{% hint style="danger" %}
⭐️ use <mark style="color:blue;">**`?.`**</mark> for <mark style="color:green;">**reading**</mark> / <mark style="color:green;">**deleting**</mark>, but <mark style="color:red;">**not**</mark> <mark style="color:red;">**writing**</mark>❗️

```javascript
user?.name           // ✅ read if exists
delete user?.name    // ✅ delete if exists.

user?.name = "John"  // ⛔ SyntaxError
// Invalid left-hand side in assignment
// this is literally `undefine = "John"`❗️ 
```
{% endhint %}

{% hint style="danger" %}
the right [bracket-notation](bracket-notation "mention") for <mark style="color:purple;">**optional chaining**</mark>

```javascript
obj ?. [ expr ]     // ✅ there's a tiny "dot" ❗️ 
obj ?  [ expr ]     // ❌ wrong syntax
```
{% endhint %}

{% hint style="danger" %}
<mark style="color:yellow;">**while**</mark> <mark style="color:purple;">**optional chaining**</mark>, <mark style="color:red;">**don't**</mark>**&#x20;**<mark style="color:yellow;">**use**</mark> <mark style="color:blue;">`(...)`</mark> <mark style="color:yellow;">**in the middle**</mark> to <mark style="color:red;">**prevent**</mark> "<mark style="color:yellow;">**short-circuiting**</mark>" from happening:exclamation:

```javascript
let a = { b: null };

a.b?.c.d,        // ✅ this is OK (⭐ "short-circuiting" works.)

// ╭─ UD ─╮      // UD: undefined
   (a.b?.c).d,   // ❌ DON'T DO THIS! (⛔ TypeError❗)
//          ^    
```
{% endhint %}
{% endtab %}

{% tab title="⭐️" %}
{% hint style="success" %}
:star: <mark style="color:purple;">**`?.`**</mark>**&#x20;**<mark style="color:yellow;">**protects**</mark> [<mark style="color:red;">**nullish**</mark>](../../../prim/nullish) <mark style="color:blue;">**`obj`**</mark>**&#x20;**<mark style="color:yellow;">**and**</mark>**&#x20;**<mark style="color:red;">**direct**</mark> <mark style="color:blue;">**`prop`**</mark><mark style="color:yellow;">**, so if**</mark> <mark style="color:blue;">**`obj`**</mark>：[<mark style="color:yellow;">**declared**</mark>](../../../../grammar/token/id/undeclared), <mark style="color:blue;">**`prop`**</mark>：[<mark style="color:yellow;">**identifier**</mark>](../../../../grammar/token/id), then it's totally <mark style="color:green;">**safe**</mark> to call <mark style="color:blue;">**`obj ?. prop`**</mark>.
{% endhint %}

{% hint style="warning" %}
:star: 「<mark style="color:orange;">**不確定存不存在**</mark>」的物件或屬性，才需要用 <mark style="color:purple;">**`?.`**</mark>
{% endhint %}

{% hint style="warning" %}
:star: <mark style="color:yellow;">**use**</mark> <mark style="color:purple;">**?.**</mark> <mark style="color:yellow;">**instead**</mark> to <mark style="color:green;">**guard**</mark>**&#x20;**<mark style="color:yellow;">**againt**</mark> [<mark style="color:blue;">**`obj.prop`**</mark>](dot-notation-.), <mark style="color:yellow;">**should**</mark> <mark style="color:blue;">**`obj`**</mark> <mark style="color:yellow;">**be**</mark> [<mark style="color:red;">**nullish**</mark>](../../../prim/nullish):exclamation:
{% endhint %}

{% hint style="success" %}
:star: <mark style="color:yellow;">**use**</mark> <mark style="color:purple;">**?.**</mark>**&#x20;**<mark style="color:yellow;">**followed by**</mark> [<mark style="color:blue;">**`??`**</mark>](../../../../grammar/op/logical/nullish-coalescing) to <mark style="color:yellow;">**provide**</mark> a <mark style="color:orange;">**default value**</mark>.

```javascript
const city = user ?. city ?? "Unknown city"
//           ╰── O.C. ──╯    ╰─ default ──╯
```
{% endhint %}

{% hint style="info" %}
:information\_source: <mark style="color:blue;">**replit.com**</mark>  (<mark style="color:green;">**v16.13.2**</mark> > <mark style="color:yellow;">**Node 14**</mark>) <mark style="color:yellow;">**supports**</mark> <mark style="color:purple;">**?.**</mark>
{% endhint %}

{% hint style="info" %}
:information\_source:[gas](../../../../../appendix/gas "mention") <mark style="color:yellow;">**supports**</mark> <mark style="color:purple;">**optional chaining**</mark>.
{% endhint %}

{% hint style="success" %}
⭐️ 注意：

當瀏覽器或 JavaScript engine 沒有此功能時，可用 [obj.prop-path](optional-chaining/obj.prop-path "mention") 或下面的方式解決：
{% endhint %}

* replit ⟩ [optional chaining](https://replit.com/@pegasusroe/JS-object-optional-chaining#index.js) ( support：❌ )

```javascript
let user = {};
user.address;           // undefined

// ⛔ TypeError: 
// ----------------------------------------------
user.address.street;    //    Cannot read property 'street' of undefined
//           ^^^^^^
```

💊 解藥：

```javascript
// ⭐️ 1. using (?:) operator
// -------------------------
// • access `user.address.street`
user.address ? user.address.street : undefined,    // undefined

// • access `user.address.street.name`
user.address
    ? (user.address.street ? user.address.street.name : null)
    : null;                                    // null

// ⭐️ 2. using (&&) operator
// -------------------------
// • access `user.address.street.name`
user.address 
    && user.address.street 
    && user.address.street.name;               // undefined
```
{% endtab %}

{% tab title="🔴" %}
* :star2:[chaining-rules](chaining-rules "mention")
* :beginner:[short-circuiting](../../../../grammar/op/term/short-circuiting "mention")
{% endtab %}

{% tab title="👥" %}
* :point\_right: [**`obj . prop`**](dot-notation-.) <mark style="color:yellow;">**/**</mark> [**`obj [ prop ]`**](bracket-notation)  (<mark style="color:red;">**non-optional**</mark>**&#x20;**<mark style="color:yellow;">**chaining**</mark>).
* :point\_right: [**`condition ? a : b`**](../../../../grammar/op/ternary/conditional-operator)
* :white\_check\_mark: <mark style="color:yellow;">**use**</mark> **`obj ?. prop`** [**`??`**](../../../../grammar/op/logical/nullish-coalescing) **`default`** to <mark style="color:yellow;">**provide**</mark> <mark style="color:orange;">**default value**</mark>.
* :adhesive\_bandage: <mark style="color:yellow;">**use**</mark> [obj.prop-path](optional-chaining/obj.prop-path "mention") <mark style="color:yellow;">**instead**</mark> <mark style="color:red;">**if**</mark> <mark style="color:purple;">**`?.`**</mark> <mark style="color:red;">**not**</mark>**&#x20;**<mark style="color:yellow;">**supported**</mark>. &#x20;
* :information\_source: [**`[...]`**](../../../../grammar/token/punctuator/brackets) <mark style="color:yellow;">**/**</mark> [**`?.`**](../../../../grammar/token/punctuator/question-dot-.) <mark style="color:yellow;">**punctuators**</mark> are used in this operator.
{% endtab %}

{% tab title="💈" %}
* replit：[optional chaining (use cases)](https://replit.com/@pegasusroe/optional-chaining-use-cases#index.js)

```javascript
function square1(x, print) {
    if (print) { print(x) }    // before optional chaining
    return x * x;
}

function square2(x, print) {
    print ?. (x);              // ⭐ optional chaining
    return x * x;
}
```
{% endtab %}

{% tab title="📗" %}
* [x] JS.info ⟩ [Optional chaining '?.'](https://javascript.info/optional-chaining)
* [ ] freeCodeCamp ⟩ [10 New JavaScript Features in ES2020 That You Should Know](https://www.freecodecamp.org/news/javascript-new-features-es2020/)
* [x] [javascript-the-definitive-guide](../../../../../master/ref/javascript-the-definitive-guide "mention") ⟩&#x20;
  * [x] 4.4 Property Access Expressions
{% endtab %}

{% tab title="📘" %}
* [Optional chaining (?.)](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Optional_chaining)
* [Expressions and operators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators) ⟩ [Property accessors](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Property_Accessors)
* Can I use [optional chaining](https://caniuse.com/mdn-javascript_operators_optional_chaining)?
{% endtab %}

{% tab title="🗣" %}
* [How to use optional chaining in Node.js 12](https://stackoverflow.com/a/59574160/5409815) (:star: supported from <mark style="color:yellow;">**Node 14.0.0**</mark>:exclamation:)
* reddit ⟩ [JS Optional Chaining Polyfill?](https://www.reddit.com/r/webdev/comments/frbct5/js_optional_chaining_polyfill/)
* [Google Apps Script - optional chaining throwing ParseError](https://stackoverflow.com/questions/64346245/google-apps-script-optional-chaining-throwing-parseerror)
{% endtab %}

{% tab title="🚧" %}
* [ ] SyntaxError: Unexpected token
{% endtab %}
{% endtabs %}
