Understanding the exclamation mark in TypeScript
Tags: typescript
Published: 2022-07-13 17:10:29

**Written by [Ibiyemi Adewakun](https://blog.logrocket.com/author/ibiyemiadewakun/)✏️**

The exclamation mark `!` is known as the non-null assertion operator in TypeScript. We will be using these terms interchangeably in this article. But what does this operator do? In this article, we will take a look at:

*   [What is the TypeScript exclamation mark?](#what-typescript-exclamation-mark)
*   [What the exclamation mark does in TypeScript](#what-exclamation-mark-does-typescript)
*   [Popular use cases for the TypeScript exclamation mark](#popular-use-cases-typescript-exclamation-mark)
    *   [Performing lookups on an array](#performing-lookups-on-array)
    *   [React refs and event handling](#react-refs-event-handling)
*   [The downside of using the exclamation mark in TypeScript](#downside-using-exclamation-mark-typescript)
*   [Alternatives to using the TypeScript exclamation mark](#alternatives-using-typescript-exclamation-mark)

## What is the TypeScript exclamation mark? <a name="what-typescript-exclamation-mark">

The non-null assertion operator tells the TypeScript compiler that a value typed as optional cannot be `null` or `undefined`. For example, if we define a variable as possibly a string or undefined, the `!` operator tells the compiler to ignore the possibility of it being undefined.

## What the exclamation mark does in TypeScript <a name="what-exclamation-mark-does-typescript">

Let’s say a variable is defined as possibly null or undefined, like so:

```typescript
let x: string | undefined 
```

Or, let’s say a function is defined to accept an optional argument, like so:

```typescript
function printString (str ?: string) { … }
```

In these cases, if we try to reference that variable as a definite type, then the TypeScript compiler would give us an error message, such as the following:

```typescript
Object is possibly 'undefined'. ts(2532)
```

We can use the non-null assertion operator to tell the compiler explicitly that this variable has a value and is not `null` or `undefined`. Let’s review a few examples to better understand the exclamation mark in TypeScript.

### Example 1: Using a variable of type `string | null` for a function that accepts `string`

Let’s say we defined a variable `word` with the type as `string | null`. This means throughout our code, `word` can either hold a `string` value or a `null` value. 

If we attempt to use a function only available to `string` types on `word`, TypeScript will reject it because there is a possibility in our code that `word` holds a `null` value type:

```typescript
let word : string | null = null
const num = 1
if (num) {
    word = "Hello World!"    
}
console.log(word.toLowerCase()) // Error: Object is possibly 'null'.ts(2531)
```

Using the `!` non-null assertion operator, we can tell TypeScript we are certain `word` will never be `null` (or `undefined`), so it can confidently apply string functions to it:

```typescript
let word : string | null = null
const num = 1
if (num) {
    word = "Hello World!"    
}
console.log(word!.toLowerCase())
```

With this small addition, the compiler no longer believes there is a possibility that `word` is null.

### Example 2: Assigning the value of an optional argument to a variable within a function

In another example, let’s say we created a function `printName` that accepts an optional argument `personName`. 

Note that defining a function argument as optional using `?:` is the same as defining type as possibly undefined. For example, `arg?: string` is the same as `arg: string | undefined`. 

If we try to reassign that optional argument `personName` to another variable of type `string`, the following would occur:

```typescript
function printName(personName?: string) {
    const fullName: string = personName 
/** 
 * Error: Type 'string | undefined' is not assignable to type 'string'. 
 * Type 'undefined' is not assignable to type 'string'.
 */
    console.log(`The name is ${fullName}`)
}
```

We can fix the TypeScript errors thrown in our snippet above using the `!` operator:

```typescript
function printName(personName?: string) {
    const fullName: string = personName! 
    console.log(`The name is ${fullName}`)
}
```

Now, the compiler understands that personName cannot be null or undefined, making it assignable to type `string`.

### Example 3: Printing the attribute of an optional object argument within a function

In our final example, we will define a type `Person` and a function `printName` that accepts an optional argument of type `Person`. Let’s see what happens if we try to use `printName` to print the name attribute of `Person`:

```typescript
interface Person {
    name: string
    age: number
}

function printName(person?: Person) {
    console.log(`The name is ${person.name}`) // Error: Object is possibly 'undefined'. ts(2532)
}
```

Let’s fix this TypeScript error using our `!` operator:

```typescript
interface Person {
    name: string
    age: number
}

function printName(person?: Person) {
    console.log(`The name is ${person!.name}`)
}
```

Note that TypeScript has an alternative for referencing attributes and functions on objects that might be null or undefined called the optional chaining operator `?.` . For example, `person?.name` or `word?.toString()` will return `undefined` if the variable is not defined or null. 

However, the optional chaining operator `?.` cannot solve the TypeScript errors in our second example, in which we tried to assign the value of a variable type `string | undefined` to a variable type `string`. Learn more about [optional chaining in the last section of this article](#alternatives-using-typescript-exclamation-mark).

## Popular use cases for the TypeScript exclamation mark <a name="popular-use-cases-typescript-exclamation-mark">

As we’ve seen in our examples, the `!` operator is very useful when we would like TypeScript to treat our variable as a solid type. This prevents us from having to handle any null or undefined cases when we are certain there is no such case. 

Now that we have seen some examples to gain a better understanding of the TypeScript exclamation mark, let’s look at some popular use cases for this operator.

### Performing lookups on an array <a name="performing-lookups-on-array">

Let’s imagine we have an array of objects and we want to pick an object with a particular attribute value, like so:

```typescript
interface Person {
    name: string
    age: number
    sex: string
}

const people: Person[] = [
  {
      name: 'Gran',
      age: 70,
      sex: 'female'
  },
  {
      name: 'Papa',
      age: 72,
      sex: 'male'
  },
  {
      name: 'Mom',
      age: 35,
      sex: 'female'
  },
  {
      name: 'Dad',
      age: 38,
      sex: 'male'
  }
]

const femalePerson = people.find(p => p.sex === 'female')
```

In our snippet above, TypeScript will define the type of `femalePerson` as `Person | undefined` because it is possible that `people.find` yields no result — in other words, that it will be undefined. 

However, if `femalePerson` has the type `Person | undefined`, we will not be able to pass it as an argument to a function expecting type `Person`. 

When we are performing lookups on these arrays, we are often confident that they have defined values, and we therefore don’t believe any undefined cases exist. Our `!` operator can save us from additional — or unnecessary — null or undefined case handling. 

Add the non-null assertion operator, like so:

```typescript
const femalePerson = people.find(p => p.sex === 'female')!
```

This would make `femalePerson` have the type `Person`.

### React refs and event handling <a name="react-refs-event-handling">

[React refs are used to access](https://blog.logrocket.com/complete-guide-react-refs/) rendered HTML DOM nodes or React elements. Refs are created using `React.createRef<HTMLDivElement>()` and then attached to the element using the `ref` attribute. 

To use React refs, we access the current attribute, `ref.current`. Until the element is rendered, `ref.current` could be `null`, so it has the following type:

```typescript
HTMLDivElement | null
```

To attach an event to `ref.current`, we would first have to handle possible `null` values. Here is an example:

```typescript
import React from 'react'

const ToggleDisplay = () => {
    const displayRef = React.createRef<HTMLDivElement>()

    const toggleDisplay = () => {
        if (displayRef.current) {
            displayRef.current.toggleAttribute('hidden')
        }
    }

    return (
        <div>
            <div class="display-panel" ref="displayRef">
                <p> some content </p>
            </div>
            <button onClick={toggleDisplay}>Toggle Content</button>
        <div>
    )
}
```

In the snippet above, we had to handle a type check of `displayRef.current` using an `if` statement before calling the `toggleAttribute` function. 

In most cases, we are sure that if the button `onClick` event is triggered, then our elements are already rendered. Therefore, there is no need for a check. This unnecessary check can be eliminated using the `!` operator, like so:

```typescript
const displayRef = React.createRef<HTMLDivElement>()

const toggleDisplay = () => displayRef.current!.toggleAttribute('hidden')

return (
        <div>
        ...
```

## The downside of using the exclamation mark in TypeScript <a name="downside-using-exclamation-mark-typescript">

The `!` operator does not change the runtime behavior of your code. If the value you have asserted is not `null` or `undefined` turns out to actually be `null` or `undefined`, an error will occur and disrupt the execution of your code. 

Remember, the difference between TypeScript and JavaScript is the assertion of types. In JavaScript we do not need or use the `!` operator because there is no type strictness. 

A JavaScript variable can be instantiated with `string` and changed to `object`, `null`, or `number` during the execution of the code. This leaves it up to the developer to handle the different cases. 

Using the `!` operator takes away TypeScript’s “superpower” of preventing runtime type errors. Therefore, it is not the best practice to use the `!` operator.

## Alternatives to using the TypeScript exclamation mark <a name="alternatives-using-typescript-exclamation-mark">

You could use optional chaining or type predicates as alternatives to non-null assertions. 

[Optional chaining is a TypeScript shorthand](https://blog.logrocket.com/16-useful-typescript-javascript-shorthands-know/#optional-chaining) that allows us easily handle the cases where the variable is defined or not. When the variable is not defined or null, the referenced value defaults to value `undefined`. Here’s an example of optional chaining:

```typescript
interface Person {
    name: string
    age: number
    sex: string
}

function printName(person?: Person): void {
    console.log('The name of this person is', person?.name)
}
```

In our example above, if `person` is undefined, our print output would be as follows:

```typescript
'The name of this person is undefined'
```

[Using type predicates in TypeScript](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates) is done by defining a function that performs a boolean test and returns a type predicate in the form `arg is Type`. Here is an example:

```typescript
interface Person {
    name: string
    age: number
    sex: string
}

function validatePerson(person?: Person) person is Person {
    return !!person
}
```

Using this type predicate, we can first validate the object before performing any further operations, like so:

```typescript
function printName(person?: Person) {
    if (!validatePerson(person)) {
        console.log('Person is invalid')
        return
    }

    console.log(`The name is ${person.name}`)
}
```

## Conclusion

TypeScript’s power over JavaScript is the [type safety it provides our code](https://blog.logrocket.com/pattern-matching-and-type-safety-in-typescript-1da1231a2e34/). However, we may sometimes want to disable TypeScript’s strict type checks — for example, for the sake of flexibility or backward compatibility. In such cases, we can use the non-null assertion operator. 

Though a useful feature, I encourage you to explore safer type assertion methods instead. You can go a step further to prevent use of this operation in your project and with your team by [adding the `typescript-eslint` package](https://blog.logrocket.com/linting-typescript-using-eslint-and-prettier/) to your project and [applying the `no-non-null-assertion` lint rule](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/docs/rules/no-non-null-assertion.md).

---

## Writing a lot of TypeScript? [Watch the recording](https://blog.logrocket.com/typescript-meetup-recap/?utm_source=social&utm_medium=organic&utm_campaign=21Q3_WB_TS-Tech-Meetup0930#utm_source=social&utm_medium=organic&utm_campaign=21Q3_WB_TS-Tech-Meetup0930) of our recent TypeScript meetup to learn about writing more readable code.

[![Write More Readable Code with TypeScript 4.4](https://blog.logrocket.com/wp-content/uploads/2021/07/typescript-4-4-more-readable-code.png)](https://blog.logrocket.com/typescript-meetup-recap/?utm_source=social&utm_medium=organic&utm_campaign=21Q3_WB_TS-Tech-Meetup0930#utm_source=social&utm_medium=organic&utm_campaign=21Q3_WB_TS-Tech-Meetup0930)

TypeScript brings type safety to JavaScript. There can be a tension between type safety and readable code. Watch the recording for a deep dive on some new features of TypeScript 4.4.