/** * @since 1.0.0 */ import { constFalse, constTrue, dual, isFunction as isFunction_ } from "@fp-ts/core/Function" import type { TypeLambda } from "@fp-ts/core/HKT" import * as readonlyArray from "@fp-ts/core/internal/ReadonlyArray" import * as contravariant from "@fp-ts/core/typeclass/Contravariant" import * as invariant from "@fp-ts/core/typeclass/Invariant" import * as monoid from "@fp-ts/core/typeclass/Monoid" import * as of_ from "@fp-ts/core/typeclass/Of" import * as product_ from "@fp-ts/core/typeclass/Product" import * as semigroup from "@fp-ts/core/typeclass/Semigroup" import type { Semigroup } from "@fp-ts/core/typeclass/Semigroup" import * as semiProduct from "@fp-ts/core/typeclass/SemiProduct" /** * @category models * @since 1.0.0 */ export interface Predicate { (a: A): boolean } /** * @category type lambdas * @since 1.0.0 */ export interface PredicateTypeLambda extends TypeLambda { readonly type: Predicate } /** * @category models * @since 1.0.0 */ export interface Refinement { (a: A): a is B } /** * Tests if a value is a `string`. * * @param input - The value to test. * * @example * import { isString } from '@fp-ts/core/Predicate' * * assert.deepStrictEqual(isString("a"), true) * * assert.deepStrictEqual(isString(1), false) * * @category guards * @since 1.0.0 */ export const isString = (input: unknown): input is string => typeof input === "string" /** * Tests if a value is a `number`. * * @param input - The value to test. * * @example * import { isNumber } from '@fp-ts/core/Predicate' * * assert.deepStrictEqual(isNumber(2), true) * * assert.deepStrictEqual(isNumber("2"), false) * * @category guards * @since 1.0.0 */ export const isNumber = (input: unknown): input is number => typeof input === "number" /** * Tests if a value is a `boolean`. * * @param input - The value to test. * * @example * import { isBoolean } from '@fp-ts/core/Predicate' * * assert.deepStrictEqual(isBoolean(true), true) * * assert.deepStrictEqual(isBoolean("true"), false) * * @category guards * @since 1.0.0 */ export const isBoolean = (input: unknown): input is boolean => typeof input === "boolean" /** * Tests if a value is a `bigint`. * * @param input - The value to test. * * @example * import { isBigint } from "@fp-ts/core/Predicate" * * assert.deepStrictEqual(isBigint(1n), true) * * assert.deepStrictEqual(isBigint(1), false) * * @category guards * @since 1.0.0 */ export const isBigint = (input: unknown): input is bigint => typeof input === "bigint" /** * Tests if a value is a `symbol`. * * @param input - The value to test. * * @example * import { isSymbol } from "@fp-ts/core/Predicate" * * assert.deepStrictEqual(isSymbol(Symbol.for("a")), true) * * assert.deepStrictEqual(isSymbol("a"), false) * * @category guards * @since 1.0.0 */ export const isSymbol = (input: unknown): input is symbol => typeof input === "symbol" /** * Tests if a value is a `function`. * * @param input - The value to test. * * @example * import { isFunction } from '@fp-ts/core/Predicate' * * assert.deepStrictEqual(isFunction(isFunction), true) * * assert.deepStrictEqual(isFunction("function"), false) * * @category guards * @since 1.0.0 */ export const isFunction: (input: unknown) => input is Function = isFunction_ /** * Tests if a value is `undefined`. * * @param input - The value to test. * * @example * import { isUndefined } from "@fp-ts/core/Predicate" * * assert.deepStrictEqual(isUndefined(undefined), true) * * assert.deepStrictEqual(isUndefined(null), false) * assert.deepStrictEqual(isUndefined("undefined"), false) * * @category guards * @since 1.0.0 */ export const isUndefined = (input: unknown): input is undefined => input === undefined /** * Tests if a value is not `undefined`. * * @param input - The value to test. * * @example * import { isNotUndefined } from "@fp-ts/core/Predicate" * * assert.deepStrictEqual(isNotUndefined(null), true) * assert.deepStrictEqual(isNotUndefined("undefined"), true) * * assert.deepStrictEqual(isNotUndefined(undefined), false) * * @category guards * @since 1.0.0 */ export const isNotUndefined = (input: A): input is Exclude => input !== undefined /** * Tests if a value is `undefined`. * * @param input - The value to test. * * @example * import { isNull } from "@fp-ts/core/Predicate" * * assert.deepStrictEqual(isNull(null), true) * * assert.deepStrictEqual(isNull(undefined), false) * assert.deepStrictEqual(isNull("null"), false) * * @category guards * @since 1.0.0 */ export const isNull = (input: unknown): input is null => input === null /** * Tests if a value is not `undefined`. * * @param input - The value to test. * * @example * import { isNotNull } from "@fp-ts/core/Predicate" * * assert.deepStrictEqual(isNotNull(undefined), true) * assert.deepStrictEqual(isNotNull("null"), true) * * assert.deepStrictEqual(isNotNull(null), false) * * @category guards * @since 1.0.0 */ export const isNotNull = (input: A): input is Exclude => input !== null /** * A guard that always fails. * * @param _ - The value to test. * * @example * import { isNever } from "@fp-ts/core/Predicate" * * assert.deepStrictEqual(isNever(null), false) * assert.deepStrictEqual(isNever(undefined), false) * assert.deepStrictEqual(isNever({}), false) * assert.deepStrictEqual(isNever([]), false) * * @category guards * @since 1.0.0 */ export const isNever: (input: unknown) => input is never = (_: unknown): _ is never => false /** * A guard that always succeeds. * * @param _ - The value to test. * * @example * import { isUnknown } from "@fp-ts/core/Predicate" * * assert.deepStrictEqual(isUnknown(null), true) * assert.deepStrictEqual(isUnknown(undefined), true) * * assert.deepStrictEqual(isUnknown({}), true) * assert.deepStrictEqual(isUnknown([]), true) * * @category guards * @since 1.0.0 */ export const isUnknown: (input: unknown) => input is unknown = (_): _ is unknown => true /** * Tests if a value is an `object`. * * @param input - The value to test. * * @example * import { isObject } from "@fp-ts/core/Predicate" * * assert.deepStrictEqual(isObject({}), true) * assert.deepStrictEqual(isObject([]), true) * * assert.deepStrictEqual(isObject(null), false) * assert.deepStrictEqual(isObject(undefined), false) * * @category guards * @since 1.0.0 */ export const isObject = (input: unknown): input is object => typeof input === "object" && input != null /** * A guard that succeeds when the input is `null` or `undefined`. * * @param input - The value to test. * * @example * import { isNullable } from "@fp-ts/core/Predicate" * * assert.deepStrictEqual(isNullable(null), true) * assert.deepStrictEqual(isNullable(undefined), true) * * assert.deepStrictEqual(isNullable({}), false) * assert.deepStrictEqual(isNullable([]), false) * * @category guards * @since 1.0.0 */ export const isNullable = (input: A): input is Extract => input === null || input === undefined /** * A guard that succeeds when the input is not `null` or `undefined`. * * @param input - The value to test. * * @example * import { isNotNullable } from "@fp-ts/core/Predicate" * * assert.deepStrictEqual(isNotNullable({}), true) * assert.deepStrictEqual(isNotNullable([]), true) * * assert.deepStrictEqual(isNotNullable(null), false) * assert.deepStrictEqual(isNotNullable(undefined), false) * * @category guards * @since 1.0.0 */ export const isNotNullable = (input: A): input is NonNullable => input !== null && input !== undefined /** * A guard that succeeds when the input is an `Error`. * * @param input - The value to test. * * @example * import { isError } from "@fp-ts/core/Predicate" * * assert.deepStrictEqual(isError(new Error()), true) * * assert.deepStrictEqual(isError(null), false) * assert.deepStrictEqual(isError({}), false) * * @category guards * @since 1.0.0 */ export const isError = (input: unknown): input is Error => input instanceof Error /** * A guard that succeeds when the input is a `Date`. * * @param input - The value to test. * * @example * import { isDate } from "@fp-ts/core/Predicate" * * assert.deepStrictEqual(isDate(new Date()), true) * * assert.deepStrictEqual(isDate(null), false) * assert.deepStrictEqual(isDate({}), false) * * @category guards * @since 1.0.0 */ export const isDate = (input: unknown): input is Date => input instanceof Date /** * A guard that succeeds when the input is a record. * * @param input - The value to test. * * @example * import { isRecord } from "@fp-ts/core/Predicate" * * assert.deepStrictEqual(isRecord({}), true) * assert.deepStrictEqual(isRecord({ a: 1 }), true) * * assert.deepStrictEqual(isRecord([]), false) * assert.deepStrictEqual(isRecord([1, 2, 3]), false) * assert.deepStrictEqual(isRecord(null), false) * assert.deepStrictEqual(isRecord(undefined), false) * * @category guards * @since 1.0.0 */ export const isRecord = (input: unknown): input is { [x: string | symbol]: unknown } => isObject(input) && !Array.isArray(input) /** * A guard that succeeds when the input is a readonly record. * * @param input - The value to test. * * @example * import { isReadonlyRecord } from "@fp-ts/core/Predicate" * * assert.deepStrictEqual(isReadonlyRecord({}), true) * assert.deepStrictEqual(isReadonlyRecord({ a: 1 }), true) * * assert.deepStrictEqual(isReadonlyRecord([]), false) * assert.deepStrictEqual(isReadonlyRecord([1, 2, 3]), false) * assert.deepStrictEqual(isReadonlyRecord(null), false) * assert.deepStrictEqual(isReadonlyRecord(undefined), false) * * @category guards * @since 1.0.0 */ export const isReadonlyRecord: ( input: unknown ) => input is { readonly [x: string | symbol]: unknown } = isRecord /** * @since 1.0.0 */ export const compose: { (bc: Refinement): (ab: Refinement) => Refinement (ab: Refinement, bc: Refinement): Refinement } = dual( 2, (ab: Refinement, bc: Refinement): Refinement => (a): a is C => ab(a) && bc(a) ) /** * @category combinators * @since 1.0.0 */ export const contramap: { (f: (b: B) => A): (self: Predicate) => Predicate (self: Predicate, f: (b: B) => A): Predicate } = dual(2, (self: Predicate, f: (b: B) => A): Predicate => (b) => self(f(b))) const imap = contravariant.imap(contramap) /** * @category instances * @since 1.0.0 */ export const Contravariant: contravariant.Contravariant = { imap, contramap } /** * @category instances * @since 1.0.0 */ export const Invariant: invariant.Invariant = { imap } /** * @since 1.0.0 */ export const tupled: (self: Predicate) => Predicate = invariant.tupled( Invariant ) as any /** * @since 1.0.0 */ export const of = (_: A): Predicate => isUnknown /** * @category instances * @since 1.0.0 */ export const Of: of_.Of = { of } /** * @since 1.0.0 */ export const unit: Predicate = of_.unit(Of) const product = (self: Predicate, that: Predicate): Predicate<[A, B]> => ([a, b]) => self(a) && that(b) const productMany = ( self: Predicate, collection: Iterable> ): Predicate<[A, ...Array]> => ([head, ...tail]) => { if (self(head) === false) { return false } const predicates = readonlyArray.fromIterable(collection) for (let i = 0; i < predicates.length; i++) { if (predicates[i](tail[i]) === false) { return false } } return true } /** * @category instances * @since 1.0.0 */ export const SemiProduct: semiProduct.SemiProduct = { imap, product, productMany } const productAll = ( collection: Iterable> ): Predicate> => (as) => { const predicates = readonlyArray.fromIterable(collection) for (let i = 0; i < predicates.length; i++) { if (predicates[i](as[i]) === false) { return false } } return true } /** * @category instances * @since 1.0.0 */ export const Product: product_.Product = { of, imap, product, productMany, productAll } /** * Appends an element to the end of a tuple. * * @since 1.0.0 */ export const appendElement: { , B>( self: Predicate, that: Predicate ): Predicate ( that: Predicate ): >(self: Predicate) => Predicate } = semiProduct.appendElement(SemiProduct) as any /** * @since 1.0.0 */ export const tuple: >>( ...predicates: T ) => Predicate] ? A : never }>> = product_.tuple(Product) /** * @since 1.0.0 */ export const struct: >>( predicates: R ) => Predicate<{ readonly [K in keyof R]: [R[K]] extends [Predicate] ? A : never }> = product_.struct(Product) /** * @since 1.0.0 */ export const not = (self: Predicate): Predicate => (a) => !self(a) /** * @since 1.0.0 */ export const or: { (that: Predicate): (self: Predicate) => Predicate (self: Predicate, that: Predicate): Predicate } = dual(2, (self: Predicate, that: Predicate): Predicate => (a) => self(a) || that(a)) /** * @since 1.0.0 */ export const and: { (that: Predicate): (self: Predicate) => Predicate (self: Predicate, that: Predicate): Predicate } = dual(2, (self: Predicate, that: Predicate): Predicate => (a) => self(a) && that(a)) /** * @category instances * @since 1.0.0 */ export const getSemigroupAny = (): Semigroup> => semigroup.make>( or, (self, collection) => a => { if (self(a)) { return true } for (const p of collection) { if (p(a)) { return true } } return false } ) /** * @category instances * @since 1.0.0 */ export const getMonoidAny = (): monoid.Monoid> => monoid.fromSemigroup(getSemigroupAny(), constFalse) /** * @category instances * @since 1.0.0 */ export const getSemigroupAll = (): Semigroup> => semigroup.make>( and, (self, collection) => a => { if (!self(a)) { return false } for (const p of collection) { if (!p(a)) { return false } } return true } ) /** * @category instances * @since 1.0.0 */ export const getMonoidAll = (): monoid.Monoid> => monoid.fromSemigroup(getSemigroupAll(), constTrue) /** * @since 1.0.0 */ export const all = (collection: Iterable>): Predicate => getMonoidAll().combineAll(collection) /** * @since 1.0.0 */ export const any = (collection: Iterable>): Predicate => getMonoidAny().combineAll(collection) // ------------------------------------------------------------------------------------- // do notation // ------------------------------------------------------------------------------------- /** * @category do notation * @since 1.0.0 */ export const bindTo: { (name: N): (self: Predicate) => Predicate<{ readonly [K in N]: A }> (self: Predicate, name: N): Predicate<{ readonly [K in N]: A }> } = invariant.bindTo(Invariant) /** * @category do notation * @since 1.0.0 */ export const Do: Predicate<{}> = of_.Do(Of) /** * A variant of `bind` that sequentially ignores the scope. * * @category do notation * @since 1.0.0 */ export const andThenBind: { ( name: Exclude, that: Predicate ): ( self: Predicate ) => Predicate<{ readonly [K in N | keyof A]: K extends keyof A ? A[K] : B }> ( self: Predicate, name: Exclude, that: Predicate ): Predicate<{ readonly [K in N | keyof A]: K extends keyof A ? A[K] : B }> } = semiProduct.andThenBind(SemiProduct)