Skip to main content

Advanced Refine Checkers

In addition to collections and primitives, more complex types can be modeled using the following combinator checkers.

or()โ€‹

Validates a value as a one of two given checkers.

// define checker
const check = or(number(), array(string()));

// result type is correct
const value: number | $ReadOnlyArray<string> = check(1);

// test a value
assert(check(1).type === 'success');
assert(check(['one']).type === 'success');
assert(check(true).type === 'failure');

union()โ€‹

Generalized version of or() to multiple values. (Note: there is currently a limitation within flow which requires an explicit type parameter for union, thus the motivation for a seperate or()).

// define checker
const check = union(number(), array(string()), boolean());

// test a value
assert(check(1).type === 'success');
assert(check(['one']).type === 'success');
assert(check(true).type === 'success');
assert(check([1]).type === 'failure');

lazy(): Recursive Collectionsโ€‹

The lazy() utility allows for defining recursive checkers.

const Person = object({
name: string(),
friends: nullable(array(lazy(() => Person))),
});

const result = Person({name: 'alice', friends: [{name: 'bob'}]});
// should succeed to validate
assert(result.type === 'success');

WARNING: recursive references in the values will not work, as the checker will stack overflow.

const Person = object({
name: string(),
friends: nullable(array(lazy(() => Person))),
});

const alice = {name: 'alice', friends: []};

// add self to own friends
alice.friends.push(alice);

// Error: will stack overflow
Person(alice);

Custom Types

custom()โ€‹

The custom utility makes it simple to define a quick custom type, such as a Class.

WARNING: Don't use this with classes requiring type parameters (such as MyClass<T>, as there is no way to validate that the type parameter is correct via instanceof).

class MyClass {}

function myClass(): Checker<MyClass> {
return custom(
value => value instanceof MyClass ? value : null,
'value is not a valid instance of MyClass'
);
}

const check = array(myClass());
assert(check([new MyClass()]).type === 'success');
assert(check([3]).type === 'failure');

asType()โ€‹

asType() will convert from one type to another. Provide a checker for the expected type and a callback function to convert to a different output type. For example, you could use this to coerce a value to an opaque type.

opaque type ID = string;

const IDChecker: Checker<ID> = asType(string(), s => (s: ID));

match()โ€‹

This checker is simply an alias for union that restricts all input checkers to produce the same output type.

Using match() and asType() you can upgrade from previous types to the latest version.

const myChecker: Checker<{str: string}> = match(
object({str: string()}),
asType(string(), str => ({str: str})),
asType(number(), num => ({str: String(num)})),
);

const obj1: {str: string} = coercion(myChecker({str: 'hello'}));
const obj2: {str: string} = coercion(myChecker('hello'));
const obj3: {str: string} = coercion(myChecker(123));

constraint()โ€‹

If you would like to require that a value passes a logical predicate, you can use constraint().

const evenNumber = constraint(
number(),
n => n % 2 === 0
);

const passes = evenNumber(2);
// passes.type === 'success';

const fails = evenNumber(1);
// fails.type === 'failure';

withDefault()โ€‹

A checker that provides a withDefault() value if the provided value is nullable.

const objPropertyWithDefault = object({
foo: withDefault(number(), 123),
});

// result will be `{foo: 123}`.
const result = check({});