/** * @access public */ export abstract class Result { constructor(public readonly value: T) {} } /** * @access public */ export class Success extends Result {} /** * @access public */ export class Failure extends Result { constructor(value: T, public readonly description: string) { super(value); } } /** * @access public */ export type Condition = (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 Assuming we'd like to create an isDefined() predicate: * ensure(`some value`, value, isDefined()); * * @example We can either use the Predicate.to factory method: * * import { Predicate } from 'tiny-types'; * * function isDefined(): Predicate { * return Predicate.to(`be defined`, (value: T) => * ! (value === null || value === undefined), * ); * } * * @example or extend the Predicate itself * * import { Predicate, Result, Success, Failure } from 'tiny-types'; * * function isDefined() { * return new IsDefined(); * } * * class IsDefined extends Predicate { * check(value: T): Result { * return ! (value === null || value === undefined) * ? new Success(value) * : new Failure(value, `be defined`); * } * } * * @access public */ export abstract class Predicate { /** * @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} 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} * * @static */ static to(description: string, condition: Condition): Predicate { return new SingleConditionPredicate(description, condition); } abstract check(value: T): Result; } /** * @access private */ class SingleConditionPredicate extends Predicate { constructor( private readonly description: string, private readonly isMetBy: Condition, ) { super(); } /** @override */ check(value: T): Result { return this.isMetBy(value) ? new Success(value) : new Failure(value, this.description); } }