Headshot of Marius Schulz
Marius Schulz Front End Engineer

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!

This post is part of the TypeScript Evolution series:

  1. TypeScript 2.0: Non-Nullable Types
  2. TypeScript 2.0: Control Flow Based Type Analysis
  3. TypeScript 2.0: Acquiring Type Declaration Files
  4. TypeScript 2.0: Read-Only Properties
  5. TypeScript 2.0: Tagged Union Types
  6. TypeScript 2.0: More Literal Types
  7. TypeScript 2.0: The never Type
  8. TypeScript 2.0: Built-In Type Declarations
  9. TypeScript 2.1: async/await for ES3/ES5
  10. TypeScript 2.1: External Helpers Library
  11. TypeScript 2.1: Object Rest and Spread
  12. TypeScript 2.1: keyof and Lookup Types
  13. TypeScript 2.1: Mapped Types
  14. TypeScript 2.1: Improved Inference for Literal Types
  15. TypeScript 2.1: Literal Type Widening
  16. TypeScript 2.1: Untyped Imports
  17. TypeScript 2.2: The object Type
  18. TypeScript 2.2: Dotted Properties and String Index Signatures
  19. TypeScript 2.2: Null-Checking for Expression Operands
  20. TypeScript 2.2: Mixin Classes
  21. TypeScript 2.3: Generic Parameter Defaults
  22. TypeScript 2.3: The --strict Compiler Option
  23. TypeScript 2.3: Type-Checking JavaScript Files with --checkJs
  24. TypeScript 2.3: Downlevel Iteration for ES3/ES5
  25. TypeScript 2.4: String Enums
  26. TypeScript 2.4: Weak Type Detection
  27. TypeScript 2.4: Spelling Correction
  28. TypeScript 2.4: Dynamic import() Expressions
  29. TypeScript 2.5: Optional catch Binding
  30. TypeScript 2.6: JSX Fragment Syntax
  31. TypeScript 2.7: Numeric Separators
  32. TypeScript 2.7: Strict Property Initialization
  33. TypeScript 2.8: Per-File JSX Factories
  34. TypeScript 2.8: Conditional Types
  35. TypeScript 2.8: Mapped Type Modifiers