19

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).

Where I see the Asserts Operator Being Used

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.


Below is a code-snippet pulled from a guide on writing unit tests with TypeScript.
function assertNonNullish<TValue>(
  value: TValue,
  message: string
): asserts value is NonNullable<TValue> {
  if (value === null || value === undefined) {
    throw Error(message);
  }
}
Examples Source


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: enter image description here


_...and this is the code for the assertion function's type (same as the image, just in code form):
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.

7
  • I found this post: stackoverflow.com/questions/19461479/… about an assertion operator. It is references the as operator as the "Assertion Operator", is as the same thing as the asserts operator? I am really confused TBH. Commented Mar 26, 2022 at 2:26
  • 1
    First link that showed up when I searched for "typescript asserts"; typescriptlang.org/play#example/assertion-functions Has a pretty good explanation in the comments, and a link to the release notes for when it was released. I'm sure with a modicum of searching in TypeScript's GitHub repo, you could find the issue that proposed it. Commented Mar 26, 2022 at 2:28
  • assertions in most languages are useful when writing tests. Commented Mar 26, 2022 at 3:40
  • 1
    Found this link to be very helpful: devblogs.microsoft.com/typescript/announcing-typescript-3-7/… Commented Mar 26, 2022 at 7:47
  • It's strange that this asserts keyword is apparently still not documented in the official handbook, and only in the release notes and playground linked to from prior comments. Commented Jan 2 at 18:30

2 Answers 2

29

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.)

Sign up to request clarification or add additional context in comments.

Comments

3

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.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.