Let's say you want to write a TypeScript function that takes a text as a parameter and returns a trimmed and lower-cased version of that text. You could use this function to normalize a host URL, for example. I've already set up tsconfig.json file, so we can go ahead right away and compile this project.
The problem is that in TypeScript null and undefined can be assigned to any type, so we can assign some string value to a string but we can also assign null and undefined at any time and the type checker will not complain about it. This is true for all types in TypeScript, including other primitive types such as numbers and Booleans.
Luckily, there is a compiler option that lets us change this behavior. We are going to head over to the tsconfig.json file and we're going to turn on the strict null checks compiler option. If we now head back to our TypeScript file we get a bunch of type errors.
This is because in strict null checking mode null and undefined are no longer valid values of any type. Now the type checker complains that type null is not assignable to type string, and the same goes for undefined.
All of these types are now non nullable types. If we want to allow the text variable to contain the value null or undefined we can use a union type and include the new type null to allow the assignment of null, and we can also include the new type undefined to include the undefined value.
Now, we no longer get a type error for the text variable. We can do the same thing with a num variable, and say that it's either a number or the value null but nothing else. Which is why the attempt to assign undefined is not type correct.
All right, let's see how we can use the null and undefined types to fix our trim and lower function. Our function should accept a string, the value null, and the value undefined. Let's add three test calls here passing a string, a value null, and a value undefined. We now have to change our type annotation to include the new types null and undefined as well.
TypeScript forces us to check the type of the text parameter before we try to access any string members on it. We can do this by adding a type guard using the type of operator. If text is not a string we simply return text unchanged.
If we now execute the code everything should work as expected. Being explicit about null and undefined in the type system can really help you understand how exactly an API works. For example, let's take document.getElementById.
Let's say you want to find the DOM element that has the ID app container and you want to store this element in a container variable. If you hover over the variable name you will see that container is either an HTML element or null. This already answers the question what document.getElementById if the element cannot be found.
Another advantage is that TypeScript will not let you dot into the container variable and do something. Visual Studio code does not give you any auto completion here. This is because we cannot do anything with container until we know that it's not null.
What we have do here is add a check, like this for example, to make sure that container is not null. Within here we can then access members to find an HTML element. There is one last feature I want to show you, and that is the non-null assertion operator which is written as an exclamation mark.
By adding the non-null assertion operator after the getElementById call we're telling the TypeScript compiler to assume that getElementById will not return null or undefined in this case.
This is why container is null typed as HTML element and no longer as HTML element or null as it was before. Therefore, we can dot into container into whatever we want without a preceding check.
Let's compile this project one last time. As you can see the type assertion has been compiled away. If document.getElementById actually does return null because the DOM element cannot be found, the call to remove in line two will result in a runtime error.
I suggest you treat the non-null assertion operator as an escape hatch and avoid it whenever possible.