Skip to Content
Course content

178: Type Assertions and Non-Null Assertions

Click on the "Edit" button in the top corner of the screen to edit your slide content.

You’ve probably hit a point in your project where TypeScript is being incredibly stubborn. You know exactly what a variable is—maybe it's a specific DOM element or a response from an API you've tested a dozen times—but TypeScript is insisting that it might be null or that it's just a generic object. In those moments, it's tempting to just force the compiler to agree with you. That's where type assertions and non-null assertions come in.

Telling the Compiler to Shut Up

The most common "quick fix" I see is the as keyword. Imagine you're grabbing a password field from a login form. document.getElementById returns a generic HTMLElement | null, but you need the value property, which only exists on HTMLInputElement. The naive way to handle this is to just assert the type and tell TypeScript to stop complaining:

const passwordInput = document.getElementById('password-field') as HTMLInputElement;
const password = passwordInput.value;

Then, you might notice that if getElementById returns null, the code still looks "correct" to the compiler, but it'll crash at runtime. To fix that, you might throw in a non-null assertion (the bang operator):

const passwordInput = document.getElementById('password-field')! as HTMLInputElement;
const password = passwordInput.value;

By adding that !, you're essentially saying, "I promise this isn't null, and if I'm wrong, feel free to crash the app." It's fast, it's concise, and in a small prototype, it feels fine.

When Your Assumptions Lie to You

Here is the problem: assertions are an escape hatch, not a solution. When you use as or !, you aren't actually changing the data; you're just lying to the type checker. I've seen countless production bugs where a developer asserted a type, then a month later, someone renamed an ID in the HTML or the API changed a field from a string to null. Suddenly, the type system—which is supposed to be your safety net—is useless because you told it to ignore the danger.

If password-field is missing from the DOM, that non-null assertion will let the code compile perfectly, but your users will get a "Cannot read property 'value' of null" error in their browser. You've effectively turned off the very feature you installed TypeScript for.

Letting the Code Prove Itself

The professional way to handle this isn't to insist you're right, but to prove you're right. Instead of assertions, use type guards. This moves the check from "compile-time guesswork" to "runtime reality."

const passwordInput = document.getElementById('password-field');

if (passwordInput instanceof HTMLInputElement) {
    // Inside this block, TypeScript automatically knows 
    // passwordInput is an HTMLInputElement and NOT null.
    const password = passwordInput.value;
    console.log(password);
} else {
    console.error("Password field was not found or is the wrong element type.");
}

I'll be honest: this is more typing. You have to write an if block and handle the "else" case. But this is the trade-off. By writing a few extra lines, you've transformed a potential crash into a handled error. You're no longer guessing; you're verifying. I always tell my juniors: if you find yourself using as or ! frequently, you're likely fighting the type system rather than using it. Use assertions only when you are dealing with external libraries that have truly broken types, or when you are absolutely certain the value is guaranteed by the environment (like a hardcoded HTML template that is checked into the same repo).




📋 Practical Task

Refactoring the User Settings Form

You have been handed a piece of legacy code for a User Settings page. The original developer used assertions to move quickly, but the app is crashing intermittently when users have certain browser extensions that modify the DOM.

Your Goal: Refactor the following code to remove all type assertions (as) and non-null assertions (!). Replace them with a type guard that ensures the element exists and is the correct type before accessing its properties.


// LEGACY CODE - DO NOT USE
function saveUserSettings() {
    const usernameInput = document.getElementById('username')! as HTMLInputElement;
    const emailInput = document.getElementById('email')! as HTMLInputElement;

    const settings = {
        username: usernameInput.value,
        email: emailInput.value
    };

    console.log("Saving settings:", settings);
}

Make sure your solution handles the case where the elements might be missing or are not input elements, preventing the app from throwing a runtime error.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.