How to Check Non-Null Values in TypeScript

Updated: January 7, 2024 By: Guest Contributor Post a comment

Introduction

TypeScript, being a strict superset of JavaScript, includes additional syntax for type-checking, which can help developers avoid null reference errors. Knowing how to properly check for non-null values is essential to take full advantage of TypeScript’s capabilities and ensure that your code is robust and error-free. In this tutorial, we’ll cover several methods and best practices for checking non-null values in TypeScript, from basic to advanced techniques.

Basic Non-Null Checks

Before you start utilizing more advanced features of TypeScript, it is critical to understand the basic methods for checking non-null values. One common approach is the use of simple conditional statements to validate that a variable is not null or undefined.

let myVar: string | null = 'Hello, TypeScript!';
if (myVar !== null) {
    console.log(myVar);
}

Using an if statement and explicitly checking for null can prevent unintended behaviour that arises from null values. Another similar approach is:

if (myVar != null) {
    console.log(myVar);
    // Note: this checks for both null and undefined
}

Truthy and Falsy Checks

In JavaScript and TypeScript, values such as 0, '' (empty string), null, undefined, false, and NaN are considered falsy. Therefore, a truthy check is one common way to ensure that a variable not only exists but also has a non-falsy value:

if (myVar) {
    console.log(myVar);
    // Prints the value if myVar is not null, undefined, 0, '', false or NaN
}

Non-null Assertion Operator (!)

TypeScript introduces a non-null assertion operator (!) that you can use when you are certain that a value will not be null or undefined at runtime. This forces the compiler to treat the operand as a non-null value.

function printLength(s: string | null): void {
    console.log(s!.length);
}

While this operator is powerful, it should be used with caution. Overusing the non-null assertion operator can lead to errors if the assumption of non-nullness is incorrect at runtime.

Type Guards and Type Assertions

TypeScript provides type guards that allow you to check the type of a variable within a block of code. A common type guard for non-null checking is the typeof operator:

if (typeof myVar === 'string') {
    console.log(myVar);
    // myVar is guaranteed to be a string and non-null here
}

Another powerful feature is the use of type assertions, which tell the compiler to consider a value as a certain type:

let unknownVar: unknown = 'This is a string';
console.log((unknownVar as string).length);
// We assure the compiler that unknownVar is a string, so this is safe

Optional Chaining and Nullish Coalescing

TypeScript 3.7 introduced optional chaining (?.) and nullish coalescing (??), which provide more concise and direct syntax for handling possibly null or undefined values.

let maybeNull: string | null = null;
console.log(maybeNull?.toUpperCase()); // Outputs: undefined if maybeNull is null or undefined
console.log(maybeNull ?? 'Default string'); // Outputs: 'Default string' if maybeNull is null or undefined

These features make it much easier to write clean and safe code for cases where a variable might hold a null value.

Using Type Predicates

To provide even more precise control over types, TypeScript allows you to define a function that serves as a user-defined type guard, commonly known as type predicates:

function isNotNull<T> (value: T | null): value is T {
    return value !== null;
}

if (isNotNull(myVar)) {
    console.log(myVar); // myVar is now strictly of type 'string' in this block
}

This function checks if a value is not null and narrows its type accordingly in the blocks where the predicate returns true.

Conclusion

In this tutorial, we’ve explored various methods to check for non-null values in TypeScript. The right method to use depends on the context and the level of certainty you have about the presence of a non-null value. By understanding these techniques, you can write safer, clearer, and more reliable TypeScript code. Always test your assumptions as you go, to ensure that you handle null and undefined values appropriately, minimizing the risk of runtime errors.