What is Typescript 's exclamation(!) mark operator, or bag and how does it work?
Avoid Object is possibly 'null'.ts(2531)
TL;DR
The bang operator tells the compiler to temporarily relax the "not null" constraint that it might otherwise demand. It says to the compiler: "As the developer, I know better than you that this variable cannot be null right now".
I think this quite a clear topic but eventually not for many that are just starting to learn Typescript or for who has to deal with some codebase that is well write with it.
Let's start from an example:
Consider this code
let referenceA: string | null | undefined = null
const n = 1
if (n) {
referenceA= "Hello My World!"
}
console.log(referenceA.toLowerCase()) // Error: Object is possibly 'null'.ts(2531)
To avoid that error we can to tell the compiler that the variable can't be Null using the Typescript "!" operator, which's called non-null assertion operator.
let referenceA: string | null | undefined = null
const n = 1
if (n) {
referenceA= "Hello My World!"
}
console.log(referenceA!.toLowerCase())
So That's the non-null assertion operator. It is a way, we developers can tell the compiler "this expression cannot be null
or undefined
here, so don't complain about the possibility of it being null
or undefined
." Sometimes the type checker is unable to make that determination itself.
It is explained in the TypeScript release notes:
A new ! post-fix expression operator may be used to assert that its operand is non-null and non-undefined in contexts where the type checker is unable to conclude that fact. Specifically, the operation x! produces a value of the type of x with null and undefined excluded. Similar to type assertions of the forms <T>x and x as T, the ! non-null assertion operator is simply removed in the emitted JavaScript code.
It is "assert" in the sense that the developer is asserting it, not in the sense that a test is going to be performed. The last line indeed indicates that it results in no JavaScript code being emitted.
So to sum-up:
The bang operator tells the compiler to temporarily relax the "not null" constraint that it might otherwise demand. It says to the compiler: "As the developer, I know better than you that this variable cannot be null right now".
Some very use case, for example are considering that unlike some other languages (eg. C#), JS (and therefore TS) does not demand that variables are initialized before use.
Or, to look at it another way, in JS all variables declared with var
or let
are implicitly initialized to undefined
. Further, class instance properties can be declared as such, so
class C { constructor() {
this.myVar = undefined; }
}
that is perfectly legal. Finally, lifecycle hooks are framework dependent; for instance Angular and React implement them differently. So the TS compiler cannot be expected to reason about them.