packages/refine/Refine_Checkers.js (62 lines of code) (raw):

/** * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * refine: type-refinement combinator library for checking mixed values * see wiki for more info: https://fburl.com/wiki/14q16qqy * * @emails oncall+monitoring_interfaces * @flow strict * @format */ 'use strict'; /** * the result of failing to match a value to its expected type */ export type CheckFailure = $ReadOnly<{ type: 'failure', message: string, path: Path, }>; /** * the result of successfully matching a value to its expected type */ export type CheckSuccess<+V> = $ReadOnly<{ type: 'success', value: V, // if using `nullable` with the `nullWithWarningWhenInvalid` option, // failures will be appended here warnings: $ReadOnlyArray<CheckFailure>, }>; /** * the result of checking whether a type matches an expected value */ export type CheckResult<+V> = CheckSuccess<V> | CheckFailure; /** * a function which checks if a given mixed value matches a type V, * returning the value if it does, otherwise a failure message. */ export type Checker<+V> = (value: mixed, path?: Path) => CheckResult<V>; /** * utility type to extract flowtype matching checker structure * * ``` * const check = array(record({a: number()})); * * // equal to: type MyArray = $ReadOnlyArray<{a: number}>; * type MyArray = CheckerReturnType<typeof check>; * ``` */ export type CheckerReturnType<CheckerFunction> = $Call< <T>(checker: Checker<T>) => T, CheckerFunction, >; /** * Path during checker traversal */ class Path { parent: ?Path; field: string; constructor(parent?: Path | null = null, field?: string = '<root>') { this.parent = parent; this.field = field; } // Method to extend path by a field while traversing a container extend(field: string): Path { return new Path(this, field); } toString(): string { const pieces = []; let current = this; while (current != null) { const {field, parent} = current; pieces.push(field); current = parent; } return pieces.reverse().join(''); } } /** * wrap value in an object signifying successful checking */ function success<V>( value: V, warnings: $ReadOnlyArray<CheckFailure>, ): CheckSuccess<V> { return {type: 'success', value, warnings}; } /** * indicate typecheck failed */ function failure(message: string, path: Path): CheckFailure { return {type: 'failure', message, path}; } /** * utility function for composing checkers */ function compose<T, V>( checker: Checker<T>, next: (success: CheckSuccess<T>, path: Path) => CheckResult<V>, ): Checker<V> { return (value, path = new Path()) => { const result = checker(value, path); return result.type === 'failure' ? result : next(result, path); }; } module.exports = { Path, success, failure, compose, };