src/predicates/Predicate.ts
/**
* @access public
*/
export abstract class Result<T> {
constructor(public readonly value: T) {}
}
/**
* @access public
*/
export class Success<T> extends Result<T> {}
/**
* @access public
*/
export class Failure<T> extends Result<T> {
constructor(value: T, public readonly description: string) {
super(value);
}
}
/**
* @access public
*/
export type Condition<T> = (value: T) => boolean;
/**
* @desc Describes a {@link Condition} that the `value` should meet.
*
* To define a custom predicate to be used with the {@link check} function
* you can either extend the {@link Predicate}, or use the {@link Predicate.to} factory method.
*
* @example <caption>Assuming we'd like to create an isDefined() predicate:</caption>
* ensure(`some value`, value, isDefined());
*
* @example <caption>We can either use the Predicate.to factory method:</caption>
*
* import { Predicate } from 'tiny-types';
*
* function isDefined<T>(): Predicate<T> {
* return Predicate.to(`be defined`, (value: T) =>
* ! (value === null || value === undefined),
* );
* }
*
* @example <caption>or extend the Predicate itself</caption>
*
* import { Predicate, Result, Success, Failure } from 'tiny-types';
*
* function isDefined<T>() {
* return new IsDefined<T>();
* }
*
* class IsDefined<T> extends Predicate<T> {
* check(value: T): Result<T> {
* return ! (value === null || value === undefined)
* ? new Success(value)
* : new Failure(value, `be defined`);
* }
* }
*
* @access public
*/
export abstract class Predicate<T> {
/**
* @desc A factory method instantiating a single-condition predicate.
* You can use it instead of extending the {Predicate} to save some keystrokes.
*
* @example
* Predicate.to(`be defined`, (value: T) => ! (value === null || value === undefined));
*
* @param {string} description - The description of the condition is used by {@link check} to generate the error
* message. The description should be similar to`be defined`,
* `be less than some value` for the error message to make sense.
* @param {Condition<V>} condition - a function that takes a value of type `V` and returns a boolean
* indicating whether or not the condition is met. For example:
* `(value: V) => !! value`
* @returns {Predicate<V>}
*
* @static
*/
static to<V>(description: string, condition: Condition<V>): Predicate<V> {
return new SingleConditionPredicate<V>(description, condition);
}
abstract check(value: T): Result<T>;
}
/**
* @access private
*/
class SingleConditionPredicate<T> extends Predicate<T> {
constructor(
private readonly description: string,
private readonly isMetBy: Condition<T>,
) {
super();
}
/** @override */
check(value: T): Result<T> {
return this.isMetBy(value)
? new Success(value)
: new Failure(value, this.description);
}
}