Assertion Functions (asserts)
Permanently narrow a type for the rest of the scope block.
The limitation of Type Guards
Standard type guards and type predicates require you to wrap your code inside an if block. This can lead to deep nesting and messy code.
An 'Assertion Function' does something different: it tells TypeScript that if the function returns normally (without throwing an error), the type is permanently narrowed for the remainder of the current scope. No if block required.
Fail fast and keep code flat
This is a common pattern in testing (e.g. assert(user !== null)) and API validation.
By asserting conditions at the very top of a function and throwing an error if they fail, the rest of your function can proceed with a perfectly narrowed, safe type without any nesting.
The 'asserts' keyword
typescript
// Notice the return type:
function assertIsString(val: any): asserts val is string {
if (typeof val !== "string") {
throw new Error("Not a string!");
}
}
function process(id: string | number) {
assertIsString(id);
// No if-block needed!
// TS knows 'id' is a string for the rest of the function.
console.log(id.toUpperCase());
}
Try it
Watch how passing the assertIsString gatekeeper permanently locks the variable's type for the rest of the function scope.
val.toUpperCase();
val is not a string. Execution stops here on failure.val: string for the rest of the block.Check yourself
Pick an answer to lock it in, then read why. Getting one wrong is part of how it sticks.
Remember this
- Assertion functions use the
asserts val is Typereturn signature. - They narrow a type for the remainder of the scope, preventing nested
ifblocks. - The function MUST throw an error if the condition fails.
- TypeScript trusts the signature blindly, so ensure your internal logic is flawless.
Done with this concept?
Mark it complete to track your progress. No login needed.