I have been toying around with TypeScript in the Node.js RTE, and I ran into a couple of type-operators (or at-least that's what I assume they are), and I don't know anything about them.
The first operator was the is operator, which I was able to infer the purpose of, and I found documentation on, the second however; was the assertion operator. I thought at first I understood what its purpose was, but it was behaving in the way I had expected it to, I also failed to promptly find any thorough documentation on the subject, though I am certain it exists, as TS is well documented (especially if you consider what it is that the TS developers have to document).
I have included a snippet, and a type, that include the asserts operator. The two examples below outline situations where I ran into the asserts operator, and didn't fully understand the code, because I failed to fully understand what the asserts operator does.
function assertNonNullish<TValue>(
value: TValue,
message: string
): asserts value is NonNullable<TValue> {
if (value === null || value === undefined) {
throw Error(message);
}
}
I also find the assert operator used to define the types of most assertion functions that included in the Node.js assertion library. This is what my hover widget shows for the assertions function deepStrictEqual's type:

type DeepStrictEqual = <T>(actual: unknown, expected: T, message?: string | Error | undefined) => asserts actual is T;
I really don't understand the purpose of using the assertion operator. I would like to figure out how it relates to typing JavaScript code, and writing unit-tests & assertion functions in TypeScript, so I can better understand when (and where) I should use it.
Assertion functions are used when you need to narrow a value to more a specific type, and you need a strong guarantee that the type meets your expectation before running other code.
Consider the following example, where you begin with a variable that might be a string or a number, and you need to use that value with a function that only accepts a number:
TS Playground
function double (n: number): number {
return n * 2;
}
function assertIsNumber (value: unknown): asserts value is number {
if (typeof value !== 'number') throw new Error('Not a number');
}
function example (value: string | number): void {
try {
assertIsNumber(value);
console.log(double(value));
}
catch (exception) {
console.error(exception);
}
}
example('hello'); // Error: Not a number
example(11); // 22
If you don't assert the type to be a number, you cannot use it with the double function:
function example (value: string | number): void {
try {
console.log(double(value)); /*
~~~~~
Argument of type 'string | number' is not assignable to parameter of type 'number'.
Type 'string' is not assignable to type 'number'.(2345) */
}
catch (exception) {
console.error(exception);
}
}
The assertion function allows you to use the value for the remainder of the scope, narrowed to be the type that you asserted it to be.
If you had only used a predicate function, then you would need to move your code into a new block (inside the matching conditional) every time you wanted to use the value as the narrowed type:
TS Playground
function double (n: number): number {
return n * 2;
}
function isNumber (value: unknown): value is number {
return typeof value === 'number';
}
function example (value: string | number): void {
if (isNumber(value)) {
console.log(double(value));
}
else {
// ...
}
}
P.S. I find that it can be more flexible to use a generic assertion function and compose it with type predicates like this:
TS Playground
function double (n: number): number {
return n * 2;
}
function assert (expr: unknown, msg?: string): asserts expr {
if (!expr) throw new Error(msg);
}
function isNumber (value: unknown): value is number {
return typeof value === 'number';
}
function example (value: string | number): void {
assert(isNumber(value), 'Not a number');
console.log(double(value));
}
For more examples of assertion functions, see this Deno standard module: https://deno.land/[email protected]/testing/asserts.ts (documentation). (I contributed the assertion typing for the one called assertExists, which is like the function in your example.)
What does the TypeScript asserts operator do? TypeScript allows you to assert that a certain condition is true, and if it is, the TypeScript type system will narrow the types accordingly in subsequent code. This is particularly useful for validating types at runtime and ensuring type safety in the rest of your program.
Example: Let's say we have a function that ensures an object has a specific property and that this property is a string. Here’s how we can use the asserts operator to achieve this:
function assertIsString(val: any): asserts val is string {
if (typeof val !== "string") {
throw new Error("Value is not a string");
}
}
function processInput(input: any) {
assertIsString(input);
// After assertIsString, TypeScript knows 'input' is a string
console.log(input.toUpperCase()); // No TypeScript error here
}
try {
processInput("hello world"); // Works fine
processInput(42); // Throws an error
} catch (e) {
console.error(e.message);
}
In the above example, the assertIsString is a custom type guard function which checks if the input value is a string.
The asserts val is string syntax tells TypeScript that if assertIsString does not throw an error, val should be treated as a string type.
In summary, the asserts operator enhances type safety by allowing you to create custom assertions that inform TypeScript's type system about the types of your variables after the assertions are made.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With