Assertion Functions

⭐ Interview Importance: MEDIUM
⏱️ Revision Time: 3 min

TL;DR

An Assertion Function is similar to a custom type guard, but instead of returning true/false to be used in an if statement, it throws a runtime error if the condition isn’t met. If the function doesn’t throw, TypeScript narrows the type for the rest of the current scope.

Mental Model

How It Works

Node.js developers frequently use the built-in assert module to ensure values are correct before proceeding.
To make TypeScript understand this pattern, you use the asserts parameterName is Type signature.
If the function completes execution without throwing, TypeScript applies that type narrowing to the variable for all subsequent lines of code in the block.

Example

// ASSERTION FUNCTION
function assertIsString(val: any): asserts val is string {
    if (typeof val !== "string") {
        throw new Error("Value is not a string!");
    }
}

function process(input: string | number) {
    // TS knows 'input' is string | number
    
    assertIsString(input);
    
    // If we reach this line, the function above didn't throw an error.
    // Therefore, TS permanently narrows 'input' to 'string' here!
    console.log(input.toUpperCase());
}

Common Interview Questions

When should you use Assertion Functions over Custom Type Guards?

Use Custom Type Guards (is) when you want to branch your logic gracefully (e.g., if it’s a fish, do X, else do Y).
Use Assertion Functions (asserts) when you are enforcing a strict contract and the program must crash if the contract is violated (e.g., verifying that a highly critical configuration file was parsed perfectly on startup).

Can you use asserts without a specific type?

Yes! You can use asserts condition. This is useful for generic assert functions.

function assert(condition: any, msg?: string): asserts condition {
    if (!condition) throw new Error(msg);
}
// assert(user !== null); 
// TS now knows 'user' is not null.