Advanced Static Types in TypeScript

This course explores the capabilities of TypeScript’s type system and shows how to use advanced static types in practice.

Watch the Course

TypeScript 2.2: Null-Checking for Expression Operands

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 type any or string.
  • If either operand of a -, *, **, /, %, <<, >>, >>>, &, |, or ^ operator is nullable.
  • If either operand of a <, >, <=, >=, or in 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!