Exclamation Mark (non-null assertion) operator in TypeScript

avatar

Borislav Hadzhiev

Mon Mar 21 20223 min read

banner

Photo by Lili Kovac

Exclamation Mark (non-null assertion) operator in TypeScript #

The exclamation mark (non-null assertion) operator removes null and undefined from the type of an expression. It is used when we we know that a variable that TypeScript thinks could be null or undefined actually isn't.

index.ts
type Employee = { id: number; name: string; }; function getEmployeeName(emp?: Employee) { return emp!.name; // 👈️ use non-null assertion } // 👇️ "Frank" console.log(getEmployeeName({ id: 1, name: 'Frank' }));

The exclamation mark (non-null assertion) operator removes null and undefined from a type.

The emp parameter in the function is marked as optional, which means that it can either be of type Employee or be undefined.

By using an exclamation mark after the variable name, we remove null and undefined from the type of the variable.

Had we not used the non-null assertion operator, we would have gotten an error when trying to access the name property.

index.ts
type Employee = { id: number; name: string; }; function getEmployeeName(emp?: Employee) { // ⛔️ Error: Object is possibly 'undefined'.ts(2532) return emp.name; } // 👇️ "Frank" console.log(getEmployeeName({ id: 1, name: 'Frank' }));

The emp parameter is possibly undefined, so we cannot safely access a property on it, because that could potentially cause a runtime error.

It's very important to note that the exclamation mark operator is simply a type assertion, in other words it does not add any type safety to your program.

It does not check if the specified variable is not null and not undefined.

The exclamation mark operator is removed in the emitted JavaScript code, so it's sole function is to remove null and undefined from the type of the variable.

When we use the non-null assertion operator, we effectively tell TypeScript that this variable is never going to be null or undefined and not to worry about it.

So, the operation myVar! produces a value of the type of myVar with null and undefined excluded.

The code snippet that used the non-null assertion operator is very similar to this code snippet that uses a simple type assertion.

index.ts
type Employee = { id: number; name: string; }; function getEmployeeName(emp?: Employee) { return (emp as Employee).name; // 👈️ type assertion } // 👇️ "Frank" console.log(getEmployeeName({ id: 1, name: 'Frank' }));

We tell TypeScript that the emp variable is going to be of type Employee and not to worry about it.

The non-null assertion operator is most commonly used when you have to get around incorrect typings of a third-party package or in any other case when we have information about the type of a value that TypeScript can't know about.

Sometimes, we really can't be sure that the specific variable is not going to be null or undefined, but need to access a property on it.

In this case, it's much safer to use the optional chaining (?.) operator.

index.ts
type Employee = { id: number; name: string; }; function getEmployeeName(emp?: Employee) { return emp?.name; // 👈️ use optional chaining } // 👇️ "Frank" console.log(getEmployeeName({ id: 1, name: 'Frank' }));

The optional chaining (?.) operator operator is like using dot notation to access a nested property of an object, but instead of causing an error if the reference is nullish, it short-circuits returning undefined.

This is why TypeScript allows us to use it to access the name property on the emp variable that could possibly be undefined.

If the variable is null or undefined, the optional chaining operator short-circuits without throwing any errors.

Alternatively, you could use an if statement that serves as a type guard.

index.ts
type Employee = { id: number; name: string; }; function getEmployeeName(emp?: Employee) { if (emp) { // 👉️ emp is type Employee here return emp.name; } // 👉️ emp is type undefined here return 'James Doe'; } // 👇️ "Frank" console.log(getEmployeeName({ id: 1, name: 'Frank' }));

Our if statement serves as a type guard, because TypeScript knows that if the condition is met, then the emp variable is of type Employee.

Otherwise, the variable has a type of undefined, because that's the only possible other type.

Use the search field on my Home Page to filter through my more than 1,000 articles.