In this lesson, we're going to look at how to perform null
checks with assertion functions. We're going to assert that a nullable value doesn't hold the value null
. In the process, we're going to learn how assertion functions integrate with TypeScript's control flow analysis.
Instructor: [0:00] I want to show you another example in which we can use an assertion function. Here, we're trying to find a DOM element that matches the ideal root. The document .getElement by ID method will return the element that has a matching ID.
[0:14] It will return the value null if no such element can be found. Therefore, our root variable is inferred to be of type HTML element or null. Let's assume we wanted to register an event listener for the click event.
[0:29] We could call it root.@EventListener and we would parse it the string click as well as a callback function. As you can see, we're getting a type error. Typescript is pointing out that our object is possibly null and that's true.
[0:45] If the element can't be found, our root variable will hold the value null. We can deal with this type error in a bunch of different ways.
[0:54] One option we have is to use typescript's non-null assertion operator which is written as a post fix exclamation mark after any expression. With this operator in place, our root variable is now inferred to be of type HTML element.
[1:09] Notice that null is no longer part of the type. However, this operator doesn't have any runtime manifestation. When we compile our typescript file to JavaScript, it is completely erased. We don't actually get any protection at runtime.
[1:25] The root variable will still contain the value null. If we try to call the add event listener method on it, we will get the famous error, cannot read property addEventListener of null. The safe way to deal with this null case is to add a null check.
[1:43] We can say that if root is null, we want to throw an error. As a descriptive error message, we should communicate that we couldn't find the DOM element we're looking for.
[1:55] With this null check in place, our type error goes away because if root is null, we throw an error in line four and never execute line seven. One more time, I want to refactor this check into an assertion function.
[2:10] I'll write a function called assert is non-nullish and it's going to be a generic function with a single type parameter T. It's going to accept a value of type T as well as an error message of type string.
[2:24] Within the body of the function, we're going to check whether value is either null or undefined. If that's the case, we're going to throw an error. Lastly, we need to add an assertion signature. This time we're going to assert that our value is of type non-nullable T.
[2:42] The non-nullable type is defined in one of the core type declaration files that ships with the typescript compiler. It is what's called a conditional type. We're going to look at conditional types in detail later in this course.
[2:56] For now, suffice it to say, that the non-nullable type removes the types null and undefined from a given type. For example, if we have the type HTML element or null or undefined, we can remove those types null and undefined using non-nullable. All that's left is type HTML element.
[3:18] We can now use our assertion function instead of the if-statement. We're going to call assert is non-nullish on our root variable. We're going to parse the same error message, couldn't find DOM element root. Notice that we don't get any type errors.
[3:35] In line 10, root is inferred to be of type HTML element or null, but if control flow makes it past our assertion function call, the root variable is now narrowed to type HTML element.
[3:49] As you've seen, assertion functions can be useful if we want to make assumptions about a certain value and get a runtime error if those assumptions aren't true.