Safe Navigation Operator? Bang! Bang Bang!!

Safe Navigation Operator? Bang! Bang Bang!!
[This article was originally published in Dev Community.]

Safe Navigation Operator

Safe navigation operator or optional chaining is now available in JavaScript and TypeScript >= v3.7🎉.

It provides easy access to deeply nested values, so checking for nullish (undefined or null) values is simplified. We avoid hitting the classic JavaScript error:

Uncaught TypeError: Cannot read property 'foo' of undefined.

A common workaround would be to short-circuit using the && operator. However, this would quickly unwind to long repetitive chains if we need to check for a deeply nested object.

const foo = a && a.b && a.b.c && a.b.c.d && a.b.c.d[0];

A lodash solution might look like

const foo = _.has(a, 'b.c.d[0]');

The safe navigation operator approach looks like

const foo = a?.b?.c?.d[0];

Returns undefined if the value does not exist.

This approach is recommended since it has now been added to the language and is browser supported. //! Except Internet Explorer

This also works with function calls, array indexes and chaining dynamic expressions.

const foo = a?.b //accessing object property
const foo = a?.[expr] //accessing through dynamic expression
const foo = arr?.[index] //accessing array index
const foo = bar?.(args) //conditional function call

Warning: This operator is not valid on the left-hand-side of an assignment operator.

const a?.b = foo; //Invalid

Note: ?. acts differently than && operator, since the && operator acts on falsy values(including 0, NaN, "", false), but ?. acts on nullish values (null and undefined).

TypeScript Bang! (non-null assertion operator)

Warning: !. is not the same as ?. . The ! postfix expression is valid in TypeScript >= v2.0. The a! operator produces a value a with null and undefined excluded. Meaning, this will explicitly tell the compiler that you are sure the type of value is not null or undefined. The compiler will thus not check if the value is null or undefined in compile time.

This could come in handy while working with maps.

const value = map.has('key') && map.get('key')!;

In this case, the compiler does not keep track that map.has() has been evaluated while evaluating map.get(). So the compiler can't determine if the map returns a safe value.

This can also be used in terms of calling a possibly undefined function and array indexes.

func!(args);
arr![0];

For example it could be used when using React refs. When using refs, the current value may be null if the element is unmounted.

JavaScript Bang! (prefix operator)

In JavaScript every value is assocaiated as either a truthy value or a falsy value. So, a bang(!) as a prefix on a value acts as a logical "not" operator on that value.

!true; //returns false
!false; //returns true

!1 //returns false
!5 //returns false

!0 //returns true
!NaN //returns true
!null //returns true
!undefined //returns true

![] //returns false (points to a reference)

const foo = "bar";
!foo //returns false

const baz = null;
!baz //returns true

JavaScript Bang Bang!!

Since a bang (!) acts as a logical "not" on a value, a double bang negates the result of the logical "not" operation. So, a double bang will first change the value to a boolean opposite, and return the opposite of that. In other words, it converts any value into a boolean type.

Note: The ! operator operates on truthy and falsy values and is not limited to nullish values. So, !'' should resolve to true.

!!true //returns true
!!false //returns false

!!null //returns false
!!undefined //returns false
!!NaN //returns false

!!34 //returns true
!!0 //returns false

!!'foo' //returns true
!!'' //returns false

!!['foo'] //returns true
!![] //returns true (points to a reference)

!!{foo: 'bar'} //returns true
!!{} //returns true (points to a reference)

Happy Hacking!