โoptional chaining (?., ?.[])
`obj ?. prop` syntax. ๐ง under construction -> SyntaxError: Unexpected token
Last updated
`obj ?. prop` syntax. ๐ง under construction -> SyntaxError: Unexpected token
Last updated
JS โฉ statement โฉ expression โฉ operator โฉ left-hand side โฉ
(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"
codepen๏ผ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)
// โฐโโโโโโโฏ โฐ๐ก๏ธโโฏ โฐ๐ก๏ธโโฏ
โญ๏ธ the variable before ?.
must be declaredโ๏ธ
// โญโโโโ๏ธโโโโฎ <----- โญ๏ธ "root" object is NEVER protectedโ๏ธ
undeclared?.address;
// โ ReferenceError: `undeclared` is not defined
?.
only protects nullish obj and direct propโ
// ๐ก๏ธ "?." 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.
โญ๏ธ use ?.
for reading / deleting, but not writingโ๏ธ
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"`โ๏ธ
the right bracket notation [] for optional chaining
obj ?. [ expr ] // โ
there's a tiny "dot" โ๏ธ
obj ? [ expr ] // โ wrong syntax
while optional chaining, don't use (...)
in the middle to prevent "short-circuiting" from happeningโ
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โ)
// ^
โญ ?.
protects nullish obj
and direct prop
, so if obj
๏ผdeclared, prop
๏ผidentifier, then it's totally safe to call obj ?. prop
.
โญ ใไธ็ขบๅฎๅญไธๅญๅจใ็็ฉไปถๆๅฑฌๆง๏ผๆ้่ฆ็จ ?.
โญ use ?. followed by ??
to provide a default value.
const city = user ?. city ?? "Unknown city"
// โฐโโ O.C. โโโฏ โฐโ default โโโฏ
โน๏ธ replit.com (v16.13.2 > Node 14) supports ?.
โน๏ธGoogle Apps Script supports optional chaining.
โญ๏ธ ๆณจๆ๏ผ
็ถ็่ฆฝๅจๆ JavaScript engine ๆฒๆๆญคๅ่ฝๆ๏ผๅฏ็จ obj.prop(path) ๆไธ้ข็ๆนๅผ่งฃๆฑบ๏ผ
replit โฉ optional chaining ( support๏ผโ )
let user = {};
user.address; // undefined
// โ TypeError:
// ----------------------------------------------
user.address.street; // Cannot read property 'street' of undefined
// ^^^^^^
๐ ่งฃ่ฅ๏ผ
// โญ๏ธ 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
๐chaining rules
๐ฐshort-circuiting
๐ obj . prop
/ obj [ prop ]
(non-optional chaining).
๐ condition ? a : b
โ
use obj ?. prop
??
default
to provide default value.
๐ฉน use obj.prop(path) instead if ?.
not supported.
replit๏ผoptional chaining (use cases)
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;
}
How to use optional chaining in Node.js 12 (โญ supported from Node 14.0.0โ)
reddit โฉ JS Optional Chaining Polyfill?
SyntaxError: Unexpected token