Null-Checking for Expression Operands in TypeScript
With TypeScript 2.2, null checking has been improved even further. TypeScript now flags expressions with nullable operands as compile-time errors.
Here's are the conditions under which TypeScript flags nullable expression operands as errors, quoted from the release notes:
- If either operand of a
+
operator is nullable, and neither operand is of typeany
orstring
. - If either operand of a
-
,*
,**
,/
,%
,<<
,>>
,>>>
,&
,|
, or^
operator is nullable. - If either operand of a
<
,>
,<=
,>=
, orin
operator is nullable. - If the right operand of an
instanceof
operator is nullable. - If the operand of a
+
,-
,~
,++
, or--
unary operator is nullable.
Let's take a look at situations in which nullable expression operands can bite us if we're not careful. Before TypeScript 2.2, this function compiled just fine:
function isValidPasswordLength(password: string, min: number, max?: number) {
return password.length >= min && password.length <= max;
}
Note that the max
parameter is optional. This means we can call isValidPasswordLength
with either two or three arguments:
isValidPasswordLength("open sesame", 6, 128); // true
isValidPasswordLength("open sesame", 6, 8); // false
The length of the password "open sesame"
is 10 characters. We therefore get back true
for the range [6,128]
and false
for the range [6,8]
. So far, so good.
If we call isValidPasswordLength
without providing a value for the max
parameter, we'd probably expect to get back true
if the password length exceeds the min
value. However, that's not the case:
isValidPasswordLength("open sesame", 6); // false
The problem here is the <= max
comparison. If max
is undefined
, <= max
will always evaluate to false
. In that case, isValidPasswordLength
will never return true
.
In TypeScript 2.2, the expression password.length <= max
is not type-correct, given that your application is running in strict null checking mode (which it should):
function isValidPasswordLength(password: string, min: number, max?: number) {
return password.length >= min && password.length <= max; // Error: Object is possibly 'undefined'.
}
So how do we fix our function to make it type-correct? One possible solution is to provide a default value for the max
parameter which only kicks in when undefined
is passed. That way, the parameter will still be optional, but will always contain a value of type number
:
function isValidPasswordLength(
password: string,
min: number,
max: number = Number.MAX_VALUE,
) {
return password.length >= min && password.length <= max;
}
There are other approaches we could choose as well, but this one works just fine. As long as we're no longer comparing max
with the undefined
value, we're good!
This article and 44 others are part of the TypeScript Evolution series. Have a look!