โž•optional chaining (?., ?.[])

`obj ?. prop` syntax. ๐Ÿšง under construction -> SyntaxError: Unexpected token

(operator) (โญ๏ธ ES2020) (๐ŸŒŸ chaining rules | table of operators )

obj?.prop returns undefined if obj is nullish.

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

๐Ÿˆฏ synonyms๏ผš"conditional property access", "optional chaining"

// โญ๏ธ 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)
//     โ•ฐโ”€โ”€โ—โ”€โ”€โ”€โ•ฏ    โ•ฐ๐Ÿ›ก๏ธโ”€โ•ฏ   โ•ฐ๐Ÿ›ก๏ธโ”€โ•ฏ 

Last updated