Object is possibly 'null' error in TypeScript [Solved]

avatar
Borislav Hadzhiev

Last updated: Feb 29, 2024
7 min

banner

# Table of Contents

  1. Object is possibly 'null' error in TypeScript
  2. Object is possibly null (document.getElementById)

# Object is possibly 'null' error in TypeScript

The error "Object is possibly 'null'" occurs when we try to access a property on an object that may have a value of null.

To solve the error, use the optional chaining operator to short-circuit if the reference is equal to null, e.g. emp?.address?.country.

object is possibly null

Here is an example of how the error occurs.

index.ts
type Employee = { address: { country: string; city: string; } | null; // ๐Ÿ‘ˆ๏ธ could be null }; const emp: Employee = { address: null, }; // โ›”๏ธ Error: Object is possibly 'null'.ts(2531) console.log(emp.address.country);

object is possibly null in typescript

The address property on the Employee type could be null which causes the error.

# Use the optional chaining (?.) operator to solve the error

We can use the optional chaining (?.) operator to get around this.

index.ts
type Employee = { address: { country: string; city: string; } | null; }; const emp: Employee = { address: null, }; // โœ… No errors console.log(emp?.address?.country); // ๐Ÿ‘ˆ๏ธ using optional chaining

use optional chaining to solve the error

The code for this article is available on GitHub

The question mark dot (?.) syntax is called optional chaining in TypeScript.

It is like using dot notation to access a nested property of an object, but instead of causing an error if the reference is nullish (null or undefined), it short-circuits returning undefined.

This approach is commonly used when fetching data from a remote API or reading data from a file, where some of the properties might not have a value.

# Use a type guard to solve the error

An alternative approach is to use a simple if statement that serves as a type guard.

index.ts
type Employee = { address: { country: string; city: string; } | null; }; const emp: Employee = { address: null, }; // ๐Ÿ‘‡๏ธ check if not null or undefined if (emp.address != null) { console.log(emp.address.country); console.log(emp.address.city); }

use type guard to solve the error

The code for this article is available on GitHub

We used an if statement to check if the emp.address property is not equal to null or undefined.

Once we enter the if block, TypeScript knows that the country and city properties are of type string.

Notice that we used the loose inequality (!=) operator, which checks for both null and undefined. You can exclusively check for null with strict not equals (!==).

The loose comparison covers both null and undefined, because in a loose comparison null is equal to undefined.

index.ts
console.log(null == undefined); // ๐Ÿ‘‰๏ธ true console.log(null === undefined); // ๐Ÿ‘‰๏ธ false

# Use a non-null (!) assertion to solve the error

You can also use the non-null assertion operator if you are sure the property could not have a value of null.

index.ts
type Employee = { address: { country: string; city: string; } | null; }; const emp: Employee = { address: { country: 'Germany', city: 'Hamburg', }, }; console.log(emp.address!.country); // ๐Ÿ‘‰๏ธ "Germany"

use non null assertion to solve the error

The code for this article is available on GitHub

The exclamation mark is the non-null assertion operator in TypeScript.

It removes null and undefined from a type without doing any explicit type-checking.

When you use this approach, you basically tell TypeScript that this value will never be null or undefined.

We used it right after the address property, so we are telling TypeScript that emp.address will never have a value of null or undefined.

# Use the logical AND (&&) operator to solve the error

If you are making a comparison in an if statement, use the logical AND (&&) operator to make sure the property is of the correct type.

index.ts
type Employee = { address: { country: string; city: string; num?: number; } | null; }; const emp: Employee = { address: null, }; if ( emp.address && typeof emp.address.num === 'number' && emp.address.num > 50 ) { console.log('success'); }
The code for this article is available on GitHub

The logical AND (&&) operator makes sure the address property is not null, that num exists on the address object and is a number before comparing it to the number 50.

This is needed because if the reference is nullish (null or undefined), the optional chaining operator (?.) will return undefined and TypeScript doesn't allow us to compare undefined to a number.

For example, this would fail:

index.ts
type Employee = { address: { country: string; city: string; num?: number; } | null; }; const emp: Employee = { address: null, }; // โ›”๏ธ could be undefined, so not allowed if (emp?.address?.num > 50) { console.log('success'); }

The result might have a value of undefined because that's the return value of the optional chaining (?.) operator when it short-circuits. TypeScript won't allow us to compare a possibly undefined value to a number.

Another common way to avoid getting the error is to use the logical AND (&&) operator when accessing the property.

index.ts
type Employee = { address: { country: string; city: string; } | null; }; const emp: Employee = { address: null, }; if (emp.address && emp.address.country) { // ๐Ÿ‘‰๏ธ emp.address.country is type string here console.log(emp.address.country.toUpperCase()); }
The logical AND (&&) operator won't evaluate the value to the right if the value to the left is falsy (e.g. null).

All of the values in the if condition have to be truthy for the if block to run.

The falsy values in JavaScript are: undefined, null, false, 0, "" (empty string), NaN (not a number).

All other values are truthy.

This is why TypeScript is able to infer the type of the emp.address.country property to be string in the if block.

# Using the typeof operator to solve the error

An even better way to get around the error in this situation is to use the typeof operator.

index.ts
type Employee = { address: { country: string; city: string; } | null; }; const emp: Employee = { address: null, }; if (emp.address && typeof emp.address.country === 'string') { // ๐Ÿ‘‰๏ธ emp.address.country is type string here console.log(emp.address.country.toUpperCase()); }
The code for this article is available on GitHub

We explicitly check if the type of the country property is a string. This is better than checking if the value is truthy because empty strings are falsy values in JavaScript (and TypeScript).

Here is an example that illustrates why using typeof is better.

index.ts
type Employee = { address: { country: string; city: string; } | null; }; const emp: Employee = { address: { country: '', city: '', }, }; if (emp.address && emp.address.country) { const result = emp.address.country; console.log(result); } else { // ๐Ÿ‘‰๏ธ else block runs console.log('โœ… This block runs'); }

The else block runs in the example.

The country property points to an empty string (falsy value), so just checking if the value is truthy might not be enough in your scenario.

It's always better to be explicit and use the typeof operator when possible. This helps us avoid some difficult-to-spot bugs.

# Object is possibly null (document.getElementById)

The error with document.getElementById occurs because the method returns null if no element with the provided id is found.

To solve the error, use a non-null assertion or a type guard to verify the variable doesn't store a null value.

object possibly null

This is the index.html file for the examples.

index.html
<!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> </head> <body> <button id="btn">Click</button> <script src="./src/index.ts"></script> </body> </html>

And here are 2 examples of how the error occurs.

index.ts
// ๐Ÿ‘‡๏ธ const button: HTMLElement | null const button = document.getElementById('btn'); // โ›”๏ธ Object is possibly 'null'.ts(2531) button.addEventListener('click', () => { console.log('button clicked'); }); // โ›”๏ธ Object is possibly 'null'.ts(2531) console.log(button.innerText);

The document.getElementById method returns null if no element with the provided id is found in the DOM.

The button variable has a type of HTMLElement | null, therefore we can't directly access a property on it, because if we access a property on a null value, we'd get a runtime error.

# Use an if statement that serves as a type guard

The best way to solve the error is to use a simple if statement that serves as a type guard.

src/index.ts
// ๐Ÿ‘‡๏ธ const button: HTMLElement | null const button = document.getElementById('btn'); // ๐Ÿ‘‰๏ธ button has type HTMLElement or null here if (button != null) { // ๐Ÿ‘‰๏ธ button has type HTMLElement here button.addEventListener('click', () => { console.log('button clicked'); }); console.log(button.innerText); }

We make sure that the button variable does not store a null value before accessing properties on it.

Once we enter the if block, TypeScript knows that the type of the button variable is HTMLElement and it couldn't be null.

# Use the optional chaining (?.) operator

An alternative approach to solve the error is to use the optional chaining (?.) operator.

src/index.ts
// ๐Ÿ‘‡๏ธ const button: HTMLElement | null const button = document.getElementById('btn'); // ๐Ÿ‘‡๏ธ using optional (?.) chaining button?.addEventListener('click', () => { console.log('button clicked'); }); // ๐Ÿ‘‡๏ธ using optional chaining (?.) console.log(button?.innerText);

The optional chaining (?.) operator short-circuits returning undefined if the reference is nullish (null or undefined).

In other words, if the button variable stores a null value, we won't attempt to access the addEventListener method and get a runtime error.

# Use a non-null (!) assertion to solve the error

We could have also used the non-null assertion (!) operator.

src/index.ts
// ๐Ÿ‘‡๏ธ const button: HTMLElement | null const button = document.getElementById('btn'); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion button!.addEventListener('click', () => { // ๐Ÿ‘ˆ๏ธ non-null assertion console.log('button clicked'); }); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion console.log(button!.innerText); // ๐Ÿ‘ˆ๏ธ non-null assertion

The exclamation mark is the non-null assertion operator in TypeScript.

It removes null and undefined from a type without doing any explicit type-checking.

When you use this approach, you basically tell TypeScript that this value will never be null or undefined.

# Use a type assertion to solve the error

This is very similar to a type assertion and should only be used when you're absolutely sure that the value is of the expected type.

src/index.ts
const button = document.getElementById('btn') as HTMLElement; button.addEventListener('click', () => { console.log('button clicked'); }); console.log(button.innerText);

Type assertions are used when we have information about the type of a value that TypeScript can't know about.

We effectively tell TypeScript that button will be an HTMLElement and not to worry about it.

We could have also used a more specific type - HTMLButtonElement because we're working with a button element.

src/index.ts
const button = document.getElementById('btn') as HTMLButtonElement; button.addEventListener('click', () => { console.log('button clicked'); }); console.log(button.innerText);

Using a more specific type enables us to access button-specific properties that don't exist in the HTMLElement interface.

The types are consistently named HTML***Element. Once you start typing HTML.., your IDE should be able to help you with autocomplete.

Some commonly used types are: HTMLInputElement, HTMLButtonElement, HTMLAnchorElement, HTMLImageElement, HTMLTextAreaElement, etc.

# Additional Resources

You can learn more about the related topics by checking out the following tutorials:

I wrote a book in which I share everything I know about how to become a better, more efficient programmer.
book cover
You can use the search field on my Home Page to filter through all of my articles.

Copyright ยฉ 2024 Borislav Hadzhiev