Invariant what is it?


Invariant comes from mathematics which defines it as a property that is guaranteed not to change. The common example being the rotation of a triangle: you can say that the triangle’s side lengths are invariant.

In computer science, invariant means the same but in a specific context. It is a condition of a program that during some period of execution is guaranteed not to change.

So why is invariant useful and how does it relate to TypeScript. There are a few libraries that essentially do the same thing:

They do nothing if a truthy value is passed or throw an error if a falsy value is passed. I think of it like an assert function, because invariant is not in my general vocabulary.

TypeScript comes into play by providing type narrowing. For example:

import function invariant(condition: any, message?: string | (() => string)): asserts conditioninvariant from "tiny-invariant";

function function fn(argument: string | null): voidfn(argument: string | nullargument: string | null) {
  function invariant(condition: any, message?: string | (() => string) | undefined): asserts conditioninvariant(argument: string | nullargument);
  var console: Consoleconsole.Console.log(...data: any[]): voidlog(
argument: string
argument
);
}

As you can see argument is of type string instead of string | null, because TypeScript knows invariant will throw if argument is falsy, it can rule out null.

A great use case for this is checking for required parameters in a context where you cannot guarantee their values, like user input. Remix make use of tiny-invariant Remix make use of tiny-invariant in their examples for this very purpose (which is where I first learned about the concept).

To see how invariant actually works we can implement it ourselves:

function function invariant(value: any): asserts valueinvariant(value: anyvalue: any): asserts value: anyvalue {
  if (value: anyvalue) {
    return;
  }

  throw new var Error: ErrorConstructor
new (message?: string | undefined, options?: ErrorOptions | undefined) => Error (+1 overload)
Error
(`${value: anyvalue} is falsy`);
} function function fn(argument: string | null): voidfn(argument: string | nullargument: string | null) { function invariant(value: any): asserts valueinvariant(argument: string | nullargument); var console: Consoleconsole.Console.log(...data: any[]): voidlog(
argument: string
argument
);
}

This uses a new keyword added to TypeScript 3.7 asserts which determines that argument must be true if it returns. Otherwise, the function will throw an error which is what we want here!