runtype
Runtime type assertions that return.
Why
runtype
was created to serve as the runtime type assertion library for @codefeathers/poly
. And as such, it makes a best effort to always return boolean, rather than silently pass and loudly throw. As a bonus, every[1] predicate is also a TypeScript type-guard.
Any throwing behaviour definitely qualifies as a bug. The absense of a type-guard, or the misbehaviour of one also qualifies as a serious bug. Make issues or PRs regarding them.
How
Installation:
npm i runtype
Usage:
import r from "@codefeathers/runtype";
const { string, number, Struct } = r;
const numbers = r.Array(number);
if (string(x)) {
}
if (Struct({ a: string, b: numbers })(x)) {
}
Read the full API docs.
Data-last
runtype
follows the data-last style. Higher order predicates always return a function that takes the input element. This makes it easier to create composed functions ahead of time while not waiting for data. Example:
import r from "@codefeathers/runtype";
const numbers = r.Array(r.number);
const numbers = x => r.Array(x, x => r.number(x));
Predicate specification
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.
You can write your own custom predicates.
To be compatible with runtype
, you MUST follow the one of the following signatures:
const SimplePredicate = (x: any): x is Type = {
};
const HOPredicate = (ctx: any) => (x: any): x is Type = {
};
Higher Order Predicates MAY accept one or more Simple Predicates and MUST return a Simple Predicate. A Simple Predicate MUST always guard a type.
TypeScript limitations
This library has taken care to meticulously type anything at all possible, but TypeScript (as of writing, v3.8.x) has limitations. We address these with adhoc solutions and TypeScript's escape hatches. While contributing to this repository, you should only resort to this as the last stand, if nothing else works. If a type is not guardable, also consider whether it is essential to runtype
.
Known limitations:
const notString = r.not(r.string);
const p = r.product([r.string, r.number, r.bool]);
In the past, with some effort and sleepless nights, we've overcome seemingly serious limitations like the variadic r.or
and r.and
types. In the future, it may be possible to type both r.not
and r.product
correctly.
Credits
Thanks to @TRGWII for helping focus my ideas and trick TypeScript into doing the right things late at night at the cost of our sanities.