In Typescript, what is the ! (exclamation mark / bang) operator?

This is technically called the non-null assertion operator. If the typescript compiler complains that a value could be null or undefined, you can use the ! operator to assert that the said value is NOT null or undefined.

In Typescript, what is the ! (exclamation mark / bang) operator?

TLDR

This is technically called the non-null assertion operator. If the typescript compiler complains about a value being null or undefined, you can use the ! operator to assert that the said value is not null or undefined.

Personal take: avoid doing this wherever possible.

What is the non-null assertion operator?

null and undefined are valid Javascript values.

The statement above holds true for all Typescript applications as well.

However, Typescript goes one step further.

null and undefined are equally valid types, e.g., consider the following:

// explicit null
let a: null 

a = null
// the following assignments will yield errors
a= undefined 
a = {}


// explicit undefined
let b: undefined 
// the following assignments will yield errors
b = null 
b = {}
Error: unassignable values other than null and undefined
Error: unassignable values other than null and undefined

See Typescript playground.

In certain cases, the Typescript compiler cannot tell whether a certain value is defined or not, i.e., not null or undefined.

For example, assuming you had a value Foo.

Foo! produces a value of the type of Foo with null and undefined excluded.

Foo! excludes null and undefined from the type of Foo
Foo! excludes null and undefined from the type of Foo

You essentially say to the Typescript compiler, I am sure, Foo will NOT be null or undefined

Let’s explore a naive example.

In standard Javascript, you may concatenate two strings with the .concat method:

const str1 = "Hello" 
const str2 = "World"

const greeting = str1.concat(' ', str2)
// Hello World

Write a simple duplicate string function that calls .concat with itself as an argument:

function duplicate(text: string | null) {
  return text.concat(text);
}

Note that the argument text is typed as string | null

In strict mode, Typescript will complain here, as calling concat with null can lead to unexpected results.

The result of calling concat with null
The result of calling concat with null

The typescript error will read: Object is possibly 'null'.(2531)

Typescript error: Object is possibly null
Typescript error: Object is possibly null

On the flip side, a rather lazy way to silence the compiler error is to use the non-null assertion operator:

function duplicate(text: string | null) {
  return text!.concat(text!);
}

Note the exclamation mark after the text variable, i.e, text!

The text type represents string | null.

text! represents just string i.e., with null or undefined removed from the variable type.

The result? You’ve silenced the Typescript error.

However, this is a silly fix.

duplicate can indeed be called with null, which may lead to unexpected results.

Note that the following example also holds true if text is an optional property:

// text could be "undefined"
function duplicate(text?: string) {
  return text!.concat(text!);
}

Pitfalls (and what to do)

When working with Typescript as a new user, you may feel like you’re fighting a lost battle.

The errors don’t make sense to you.

Your goal is to remove the error and move on with your life as swiftly as you can.

However, you should be careful with using the non-null assertion operator.

Silencing a Typescript error doesn’t mean there may not still be an underlying issue—if unaddressed.

As you saw in the earlier example, you lose every relevant Typescript safety against wrong usages where null and undefined could be unwanted.

So, what should you do?

If you write React, consider an example you’re likely familiar with:

const MyComponent = () => {
   const ref = React.createRef<HTMLInputElement>();
	
   //compilation error: ref.current is possibly null
   const goToInput = () => ref.current.scrollIntoView(); 

    return (
       <div>
           <input ref={ref}/>
           <button onClick={goToInput}>Go to Input</button>
       </div>
   );
};

In the example above (for those who do not write React), in the React mental model, ref.current will certainly be available at the time the button is clicked by the user.

The ref object is set soon after the UI elements are rendered.

Typescript does not know this, and you may be forced to use the non-null assertion operator here.

Essentially, say to the Typescript compiler, I know what I’m doing, you don’t.

const goToInput = () => ref.current!.scrollIntoView();

Note the exclamation mark !.

This “fixes” the error.

However, if in the future, someone removes the ref from the input, and there were no automated tests to catch this, you now have a bug.

// before
<input ref={ref}/>

// after
<input />

Typescript will be unable to spot the error in the following line:

const goToInput = () => ref.current!.scrollIntoView();

By using the non-null assertion operator, the Typescript compiler will act as if null and undefined are never possible for the value in question. In this case, ref.current.

Solution 1: find an alternative fix

The first line of action you should employ is to find an alternative fix.

For example, often you can explicitly check for null and undefined values.

// before 
const goToInput = () => ref.current!.scrollIntoView();

// now 
const goToInput = () => {
  if (ref.current) {
   //Typescript will understand that ref.current is certianly 
   //avaialble in this branch
     ref.current.scrollIntoView()
  }
};

// alternatively (use the logical AND operator)
const goToInput = () => ref.current && ref.current.scrollIntoView();

Numerous engineers will argue over the fact that this is more verbose.

That’s correct.

However, choose verbose over possibly breaking code being pushed to production.

This is a personal preference. Your mileage may differ.

Solution 2: explicitly throw an error

In cases where an alternative fix doesn’t cut it and non-null assertion operator seems like the only solution, I typically advise you throw an error before doing this.

Here’s an example (in pseudocode):

function doSomething (value) {
   // for some reason TS thinks value could be  
   // null or undefined but you disagree
   
  if(!value) {
    // explicilty assert this is the case 
    // throw an error or log this somewhere you can trace
    throw new Error('uexpected error: value not present')
  } 

  // go ahead and use the non-null assertion operator
  console.log(value)
}

A practical case where I’ve found myself sometimes doing this is while using Formik.

Except things have changed, I do think Formik is poorly typed in numerous instances.

The example may go similar to you've done your Formik validation and are sure that your values exist.

Here’s some pseudocode:

<Formik 
  validationSchema={...} 
  onSubmit={(values) => {
   // you are sure values.name should exist because you had 
   // validated in validationSchema but Typescript doesn't know this

   if(!values.name) {
    throw new Error('Invalid form, name is required')		
   } 
   console.log(values.name!)
}}>


</Formik>

In the pseudocode above, values could be typed as:

type Values = {
  name?: string
}

But before you hit onSubmit, you’ve added some validation to show a UI form error for the user to input a name before moving on to the form submission.

There are other ways to get around this, but if you find yourself in such a case where you’re sure a value exists but can’t quite communicate that to the Typescript compiler, use the non-null assertion operator. But also add an assertion of your own by throwing an error you can trace.

How about an implicit assertion?

Even though the name of the operator reads non-null assertion operator, no “assertion” is actually being made.

You’re mostly asserting (as the developer), that the value exists.

The Typescript compiler does NOT assert that this value exists.

So, if you must, you may go ahead and add your assertion, e.g., as discussed in the earlier section.

Also, note that no more Javascript code is emitted by using the non-null assertion operator.

As stated earlier, there’s no assertion done here by Typescript.

Consequently, Typescript will not emit some code that checks if this value exists or not.

The Javascript code emitted will act as if this value always existed.

Conclusion

TypeScript 2.0 saw the release of the non-null assertion operator. Yes, it’s been around for some time (released in 2016). At the time of writing, the latest version of Typescript is v4.7.

If the typescript compiler complains about a value being null or undefined, you can use the ! operator to assert that the said value is not null or undefined.

Only do this if you’re certain that is the case.

Even better, go ahead and add an assertion of your own, or try to find an alternative solution.

Some may argue that if you need to use the non-null assertion operator every time, it’s a sign you’re poorly representing the state of your application state via Typescript.

I agree with this school of thought.

Get a Free Typescript Book?

image-148
Build strongly typed Polymorphic React components 

Get this book for free