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 condition
invariant from "tiny-invariant";
function function fn(argument: string | null): void
fn(argument: string | null
argument: string | null) {
function invariant(condition: any, message?: string | (() => string) | undefined): asserts condition
invariant(argument: string | null
argument);
var console: Console
console.Console.log(...data: any[]): void
log(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 value
invariant(value: any
value: any): asserts value: any
value {
if (value: any
value) {
return;
}
throw new var Error: ErrorConstructor
new (message?: string | undefined, options?: ErrorOptions | undefined) => Error (+1 overload)
Error(`${value: any
value} is falsy`);
}
function function fn(argument: string | null): void
fn(argument: string | null
argument: string | null) {
function invariant(value: any): asserts value
invariant(argument: string | null
argument);
var console: Console
console.Console.log(...data: any[]): void
log(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!