Null references are probably the most common cause of runtime errors in many programming languages. New JavaScript language features and compiler parameters in TypeScript help to avoid the “trillion dollar mistake”.
In JavaScript, these errors are likely to occur much more frequently due to the lack of static typing. Two operators introduced with ECMAScript 2020 facilitate the handling of null values. TypeScript offers a compiler parameter to avoid null reference exceptions whenever possible.
In many programming languages it is possible to assign the value “null” to a variable that has been declared with an object type, for example in C #:
Person person = null;
However, this approach leads to a problem when developers assume that a suitable object is in the variable and try to dereference properties of it. Then a null reference exception occurs during runtime:
person.Name
// NullReferenceException!
Errors related to null references are likely to be the most common programming errors at runtime. Tony Hoare, the inventor of the zero reference, apologized publicly in 2009 for his “trillion-dollar mistake”, the introduction of the zero reference in ALGOL W. In modern languages such as Swift or Kotlin, the above assignment of “nil” or ” null “not allowed by default, only for optional or nullable types:
let person: Person? = nil
Many languages have since followed suit: In C # and TypeScript, developers can opt-in to a stricter zero check and significantly reduce the risk of zero reference exceptions at runtime. With the ECMAScript 2020 language level, some new operators have been introduced in JavaScript to simplify the handling of null values. TypeScript developers also benefit from this.
Two null values in JavaScript
First of all, it should be noted that JavaScript has two “null” values, “null” itself and “undefined”. While “null”, according to the language standard, represents the deliberate lack of a value, i.e. it tends to be set explicitly, “undefined” is the value when a variable has not yet been assigned a value, i.e. it can also arise implicitly. In JavaScript it is possible to get non-existent properties of an object, but not to get properties of “null” or “undefined”:
const foo = { bar: null };
foo.bar // null
foo.baz // undefined
foo.baz.qux // TypeError: Cannot read properties of
// undefined ≈ NullReferenceException
Ternary expressions: the classic
The first, always possible way to deal with such values in JavaScript and TypeScript is the ternary expression known from many languages:
const name = person ? person.name : 'Keine Person gewählt';
However, caution is advised here because the condition in front of the selection operator ?
is interpreted as a Boolean value in contrast to the operators presented below: In JavaScript, not only false
, null
or undefined
when converting to Boolean to false
derived, but also the values 0, NaN, or the empty string. Therefore, the condition can also apply in cases that may not be intended.
Optional Chaining: Elvis has entered the building
With the language level ECMAScript 2020 the so-called optional chaining (operator ?
.) introduced in JavaScript. This concept has many names: it is also known as Safe Navigation; the operator is often referred to as the ELVIS operator, which is an acronym for Evaluate Left Value If Set should stand, or, turned 90 degrees to the right, reminiscent of Elvis Presley’s Schmalztolle.
Expressions are evaluated from left to right. With optional chaining, dereferencing only takes place if the evaluated property not the values null
or undefined
contains. Otherwise, the value undefined
returned.
const foo = { bar: 3, baz: null };
foo?.bar // 3
foo?.baz // null
foo?.baz?.qux // undefined
A null reference exception no longer occurs here at runtime, even if non-existent properties (such as “qux” from the “baz” property) are retrieved. What is less well known is that optional chaining also works for index accesses and method calls. The brackets for the list of parameters or the index expression must be preceded by a point:
dialog.canClose?.()
person.awards?.[0]
Nullish Coalescing: Defining Fallbacks
When using the nullish coalescing operator (??
) the left expression is returned if it is not null
or undefined
is, otherwise the right expression. This operator has been around for a long time in C #. It is typically used to define standard or fallback values if the value you are looking for is not defined:
const people = response.people ?? [];
There is also a suitable variant for assignments for this operator:
people ??= []
Caution should be exercised in all cases
In all of the cases shown, it must be remembered that the branches are implicit in each case. The cyclomatic complexity is consequently very high in a very compact code excerpt. With the help of optional chaining and nullish coalescing, even more complicated cases can be handled in one line of code. The fallback value “Unknown Name” is defined below if either “person” or “person.name” null
or undefined
are:
const name = person?.name ?? 'Unbekannter Name';
While the operators shown can avoid errors at runtime, at least in JavaScript the problem remains that developers need to know which variable can contain null values. This is where TypeScript comes in to help.
TypeScript helps with strictNullChecks
TypeScript offers a compiler option called strictNullChecks
which delivers the behavior of Swift and Kotlin described above in TypeScript as well. The value undefined
may only be assigned if a nullable type is used:
const anna: Person = null; // Unzulässig mit strictNullChecks
const peter: Person | null = null; // Zulässig mit strictNullChecks
When using nullable types, developers must first check that an object is actually stored. Otherwise, dereferencing is not allowed:
peter.name // Unzulässig mit strictNullChecksif (peter) {
peter.name // Zulässig mit strictNullChecks
}
If developers are sure that a value is in a variable of nullable type, the non-null assertion operator also applies – an exclamation point, appropriately. Potentially unsafe access may then be carried out again:
peter!.name // Zulässig mit strictNullChecks
However, since this operator cancels the protection again, it should be avoided if possible. Caution is still required because despite strictNullChecks, the initialization of a class property is not mandatory:
private anna: Person; // Zulässig mit strictNullChecks
In order to get out of this source of error, the compiler option strictPropertyInitialization
to be activated. Then all class properties that have non-nullable types must always be initialized with a value. We recommend that you use this compiler option when using the strictNullChecks
to activate at the same time. Then the source code should be largely protected from null reference exceptions. With TypeScript, this only applies at compile time, because no type checking takes place during execution in the browser. Problems can arise, for example, if the server does not respond with the response that the TypeScript code expects.
The later migration of a codebase to strictNullChecks
and strictPropertyInitialization
not infrequently involves a lot of effort and also makes many subtle bugs visible. For this reason, it is advisable to activate these compiler options for new projects from the outset or not to postpone an upcoming migration for a long time.
Conclusion
Even if it introduces a lot of punctuation marks into the codebase: Optional chaining, nullish coalescing and nullable types improve the robustness of programs written in JavaScript or TypeScript by helping to avoid one of the most common runtime errors. For larger applications, the introduction of a stricter zero check is strongly recommended. Since version 12, the SPA framework Angular also switches automatically for all new projects strictNullChecks
and strictPropertyInitialization
a.