The exclamation mark in TypeScript
In many scripting languages, developers use the exclamation mark as a not operator. But when working with TypeScript, the exclamation mark acts as a non-null assertion operator. This non-null assertion will remove null and undefined values.
In this article, I will discuss several uses of the exclamation mark in TypeScript.
How does the exclamation mark behave in TypeScript?
Let’s see some simple examples of how the exclamation mark is used in TypeScript.
When defining a variable
If we define a string-type variable as string | null, it means that the variable holds a string or null value. But suppose we define a function that accepts only a string type as a parameter. In that case, the TypeScript compiler will reject our variable value since there is a possibility that it will have a null value. Refer to the following code:
let stringWord: string | null const number =1 if(number){ stringWord = “Test word” } console.log(stringWord.toUpperCase())
In this case, the following error will appear:
Error: Object is possibly ‘null’.ts(2531)
However, if you use the non-null assertion operator, you can convey to the TypeScript compiler that the stringWord variable is never null or undefined.
This is what the modified code looks like:
let stringWord: string | null const number =1 if(number){ stringWord = “Test word” } console.log(stringWord!.toUpperCase()) // added the exclamation mark
Syncfusion JavaScript UI controls are the developers’ choice to build user-friendly web applications. You deserve them too.
When passing a value as an optional argument in a function
Consider a function we use to get a student’s details. The function will accept the parameter studentName.
Function getDetails(studentName ?: string){ const name: string = studentName const age: number = 25 console.log(“Name : ${name}”) console.log(“Age: ${age}”) }
In the above code example, you can see the use of ? after the parameter. In TypeScript, the question mark is used to define an argument as optional. It is the same as specifying the type as undefined. It is similar to studentName: string | undefined.
The above function will throw the following error.
Error: Type ‘string | undefined’ is not assignable to type ‘string’. Type ‘undefined’ is not assignable to type ‘string’.
The reason for this error is that the compiler considers studentName as a null value or undefined value. We can solve this issue by adding an exclamation mark after the studentName and making it a string type.
Function getDetails(studentName ?: string){ const name: string = studentName! const age: number = 25 console.log(“Name : ${name}”) console.log(“Age: ${age}”) }
When getting the attribute of an optional object within a function
Here, we define a type Student and the function getName that accepts an argument of type Student.
type Student ={ sid : number; name: string; }; function getName(std ?: Student){ console.log("Name of the student is ${std.name}") }
In this function, the std parameter is marked as an optional parameter, so we cannot safely access the property of std. It can be either a Student type or an undefined type. If we don’t use the non-null assertion operator, we will get the following error:
Error: Object is possibly 'undefined'. ts(2532)
To avoid this error, inform the compiler that this variable will never be undefined or null. Introducing the non-null assertion to this code will solve this error.
type Student ={ sid : number; name: string; }; function getName(std ?: Student){ console.log("Name of the Student is ${std!.name}"); }
Every property of the Syncfusion JavaScript controls is completely documented to make it easy to get started.
Use cases for the exclamation mark as a non-null assertion operator
Let’s discuss some use cases for the exclamation mark as a non-null assertion in TypeScript.
Search for an item that exists in a list
Let’s consider a scenario where specific items exist in a list and you need to access those elements and check them.
interface Student { sid : number; name: string; }; const students: Student[] =[ { sid : 1, name: "Alex", }, { sid : 2, name: "Gerome", }, { sid : 3, name: "Bennet", }, ]; const getStudent =( sid: number) => { return students.find((students) => students.sid ==sid); }; const students = getStudent(1);
In the above code example, we have a list of students, and we need to find the student details based on the student ID, sid. Everything looks fine in the code but imagine if the type of the variable students can be undefined. In this case, we cannot pass sid as an argument.
Most of the time, we feel confident that these arrays have only defined values when we perform a search on them. In practice, though, we should prepare the application for handling null or undefined cases too. This can be easily achieved using the ! operator.
The following code snippet shows how to address this case using the non-null assertion operator.
const getStudent =( sid: number) => { return students.find((students) => students.sid ==sid)!; //Add the exclamation mark }; const students = getStudent(1);
Handling React refs in TypeScript
React refs provide a way to access DOM nodes or React elements. When we use React refs, we have to access the current attribute, ref.current.
This attribute is in a null state until the rendering happens, or it can take the following type:
HTMLDivElement | null
Let’s explore the way to handle this null using an example.
// some code const App = () => { const ref = useRef(null); const handleClick = () => { if(ref.current) { console.log(ref.current.getBoundingClientRect()); } }; return ( <div className="App" ref={ref}> {/* ... */} <button onClick={handleClick}>OK</button> </div> ); };
Here, we created a component with access to the div element with the App class DOM node. Clicking on the Ok button will display the size of the component and its position in the viewport.
There are times when the UI element will not be rendered when a click action is performed on the OK button. This might lead to an exception during runtime. The ! operator can be used to eliminate this pointless check. Refer to the following code example:
const handleClick = () => { if(ref.current) { console.log(ref.current!.getBoundingClientRect()); } };
To make it easy for developers to include Syncfusion JavaScript controls in their projects, we have shared some working ones.
When is the exclamation mark assertion not useful?
Even though we added the ! operator to handle null and undefined issues; this operator does not change any runtime behavior of your code. Errors can still disrupt the execution even if you handle the null and undefined issues with the non-null assertion operator.
Unlike JavaScript, TypeScript uses assertions for types. JavaScript variables can handle type issues during execution time. So, handling type issues depends on the scenario developers face when dealing with such problems.
The ! operator is TypeScript’s main advantage for preventing runtime errors. Therefore, we need to focus more on adding additional checks to handle non-null issues since the ! operator is not the best practice for handling such matters.
Alternatives to the exclamation mark operator in TypeScript
You can use a few other options you can use as an alternative to the ! operator in TypeScript.
Type predicates
In TypeScript, type predicates define a function and perform a Boolean test which returns a type predicate. It will handle the null and undefined issues beforehand.
interface Student { sid : number; name: string; }; function validateStudent(std?: Student) std is Student{ return !! std }
We have to add the type predicate as shown in the above code sample. After that, we can perform the validation before the core functions in our code.
function getName(std ?: Student){ if (!validateStudent(std)) { console.log('Student is invalid'); return } console.log("Name of the Student is ${std.name}"); }
Optional chaining
Optional chaining changes the reference value defaults to undefined even if the variable is undefined or null. Let’s see the following example:
interface Student { sid : number; name: string; }; function getName(std ?: Student): void { console.log("Name of the Student is ", std?.name); }
If the std is undefined, it will display the following output:
Name of the Student is undefined
Syncfusion JavaScript controls allow you to build powerful line-of-business applications.
Conclusion
TypeScript’s superpower is its type safety. However, in some cases, we have to disable the strict type checks. If you want to add more flexible code components to your code, you can use the non-null assertion-type operations covered in this blog.
Even though ! is a helpful operator, developers must be careful when using it, or they may get stuck with unknown runtime errors. But if you like your code less lengthy and with fewer validations, you are welcome to use the exclamation mark operator.
Thank you for reading!
Syncfusion’s Essential JS 2 is the only suite you will need to build an app. It contains over 65 high-performance, lightweight, modular, and responsive UI components in a single package. Download a free trial to evaluate the controls today.
If you have questions or comments, contact us through our support forums, support portal, or feedback portal. We are always happy to assist you!