A case for the void operator
I've recently fallen in love with the delightful expressiveness of the JavaScript void
operator.
If you don't know what the void
operator is you can read about it in MDN but you don't need to know anything to understand this article.
Expressiveness
Taka a look at the following example usage.
const handleEvent = event => void sideEffect(event);
Even if you've never seen a void
operator used in javascript you can likely tell handleEvent
always returns undefined
.
The same code without the void operator would look like this.
const handleEvent = event => {
sideEffect(event);
};
I claim the former is more expressive than the latter.
The number of lines has nothing to do with it. We could write a one-liner without the void operator.
const handleEvent = event => { sideEffect(event); };
The reason void
is more expressive lies in the specificity of the syntax.
void
is only ever used for voiding the value of an expression.
An arrow function body can be used to void a value but it can also be used to group statements of a function.
Voiding a value is the only possible reason a developer would ever use the void
operator.
It eliminates ambiquity from the code while a function body leaves some in.
Maintainability
Expressive code is easier to maintain and less prone to regression.
Let's assume our handleEvent
example goes through some changes, as code tends to do.
It's extended with another function call statement.
const handleEvent = event => {
sideEffect(event);
otherSideEffect(event);
};
Let's say otherSideEffect
wasn't the greatest feature and soon it's removed again.
const handleEvent = event => sideEffect(event);
Whoops!
We've introduced regression.
handleEvent
now returns the return value of sideEffect
instead of undefined
.
Of course ultimately the developer who made the last change is at fault but I'm scarcely interested in the blame game when we can prevent regression by writing expressive code.
Let's say we wrote handleEvent
using the void
operator instead.
const handleEvent = event => void (
sideEffect(event),
otherSideEffect(event)
);
The natural, unambiguous way to remove otherSideEffect
is to revert back to the original.
const handleEvent = event => void sideEffect(event);
No developer is ever going to mess up that change.
Typescript
Void return types are susceptible to falling through the cracks of TypeScript's type checking. It's not Typescript's fault.
The types void
and undefined
are commonly used interchangeably and for an arguably good reason.
If the value is voided then void
is semantically correct.
It makes APIs less strict and therefore reduces time spent resolving tiresome type errors.
It comes with its price, though, which I already elucidated in the regression example above.
The void
operator gives us the flexibilty of the void
type and the safety of undefined
.
Abuse
I've only covered the void
operator in the context of voiding side effect return values.
I've done so because that's the only appropriate use of void
in source code.
In addition to function calls, like the cases I showed in examples, object property mutations are an appropriate use of void
as they always have side effects.
You may have seen void
used in minified code to denote undefined
using less characters: void 0
.
Any case where the expression being voided does not potentially create side effects is only appropriate in transpilers and code golf.