Trimming JavaScript: ditch verbosity, gain readability
Dan Bar-Shalom
Posted on April 21, 2024
Are you familiar with expressions like these:
a !== undefined && a !== null ? a : b
if (a.x === undefined || a.x === null) { a.x = b }
{ foo: foo, bar: bar }
a ? a.b : undefined
function getX(o) { return o.x }
What do they all have in common? They are highly verbose, and all have a nice shorthand alternatives. Let's see how we can improve each one of these:
Nullish coalescing operator ??
This operator checks the value of the left hand-side operand - if it's null or undefined, it returns the right hand-side operand. Otherwise it returns the left hand-side, without evaluating the right hand-side.
This allows simplifying the following expression:
a !== undefined && a !== null ? a : b
To this:
a ?? b
Unlike ||
operator that checks for any falsy value, this operator only checks for nullish values - meaning null
or undefined
. Therefore it is safer to use in cases that a
is a boolean which can be false
or a number which can be 0
, and then a || b
will return the value of b
Nullish coalescing assignment ??=
This operator only evaluates the right operand and assigns to the left if the left operand is null or undefined.
So we can simplify this expression:
if (a.x === undefined || a.x === null) {
a.x = b
}
to this:
a.x ??= b
JSON shorthand syntax
The shorthand syntax was introduced with ES6 and is already pretty common.
Use it to shorten this:
const name = 'john doe'
const age = 32
const talk = text => void console.log(text)
const person = {
name: name,
age: age,
talk: talk
}
to that:
const name = 'john doe'
const age = 32
const talk = text => void console.log(text)
const person = {
name,
age,
talk
}
Optional chaining ?.
This operator accesses an object's property just like .
does. However, unlike .
, if the object is null or undefined, the expression will return undefined
instead of throwing an error.
This allows replacing this code:
a ? a.b : undefined
with that:
a?.b
This operator is pretty strong, and can be used in various ways:
- Chaining:
a?.b?.c?.d
- Call interface method:
someInterface.customMethod?.()
Note that in this case, ifcustomMethod
exists, but it is not a function, you'd still get an exceptionsomeInterface.customMethod is not a function
- Access dynamic property with bracket notation:
x?.[propname]
- Access array items:
array?.[50]
- even if array is nullish, you'd getundefined
instead of an exception
Arrow functions
There's a lot to say about arrow functions, but in the context of this post I want to focus on a specific use - 1 liner methods that calculate something or access some property. They have 2 attributes that help with code shortening:
- Arrow functions that do not have a block body wrapped with curly brackets
{/*...*/}
have an implicitreturn
. - in a single-param arrow function you do not have to put the argument in parentheses Meaning, you can change this:
function getX(o) {
return o.x
}
with this
const getX = o => o.x
This is super useful, for example, for mapping functions.
Note that there are some caveats though:
- Arrow functions do not have
this
- Arrow functions do not have
arguments
. You can use spread operator instead. E.g(...args) => args[0]
- Arrow functions cannot call
super
or be used as constructors
Posted on April 21, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.