G (Guards)
Runtime type checks that narrow the TypeScript type.
type Guard<A> = Refinement<unknown, A>Type checks
Section titled “Type checks”isString(x: unknown): x is stringisNumber(x: unknown): x is numberisFiniteNumber(x: unknown): x is numberisNonBlankString(x: unknown): x is stringisBoolean(x: unknown): x is booleanisBigInt(x: unknown): x is bigintisSymbol(x: unknown): x is symbolisFunction(x: unknown): x is FunctionisDate(x: unknown): x is DateisError(x: unknown): x is ErrorisPromise(x: unknown): x is Promise<unknown>isArray(x: unknown): x is unknown[]isObjectType(x: unknown): x is objectisPlainObject(x: unknown): x is Record<string, unknown>isArrayOf<A>(guard: Guard<A>): Guard<A[]>isRecordOf<A>(guard: Guard<A>): Guard<Record<string, A>>isPlainObject rejects arrays and class instances. isObjectType is just a typeof check.
isRecordOf accepts only plain objects and checks every own enumerable string-keyed value. Empty
arrays and records are valid because every contained value satisfies the supplied guard.
Nullability
Section titled “Nullability”isNil(x: unknown): x is null | undefinedisNotNil<T>(x: T | null | undefined): x is TisNull(x: unknown): x is nullisUndefined(x: unknown): x is undefinedisNullish(x: unknown): x is null | undefinedisNonNull<T>(x: T | null): x is TisNonNullish<T>(x: T | null | undefined): x is TisDefined<T>(x: T | undefined): x is TEquality
Section titled “Equality”isDeepEqual(a: unknown, b: unknown): booleanisShallowEqual(a: unknown, b: unknown): booleanisStrictEqual(a: unknown, b: unknown): booleanisEmpty(x: unknown): booleanisEmptyish(x: unknown): boolean // null, undefined, or emptyisTruthy(x: unknown): booleanis<T>(ctor: new (...args: any[]) => T, val: unknown): val is TpropIs<T>(ctor: new (...args: any[]) => T, prop: string, obj: Record<string, unknown>): booleanExamples
Section titled “Examples”import { pipe } from '@stopcock/fp'import * as A from '@stopcock/fp/array'import * as G from '@stopcock/fp/guard'
// narrow unknown API dataconst data: unknown[] = [1, 'hello', null, { a: 1 }]data.filter(G.isNumber) // [1], typed as number[]data.filter(G.isNonNullish) // [1, 'hello', { a: 1 }]
// validate form fieldif (G.isString(input) && !G.isEmpty(input)) { submit(input)}
// filter mixed arrays in pipepipe( rawValues, A.filter(G.isNumber), // unknown[] → number[] A.filter((n) => n > 0),)
// deep equality checkG.isDeepEqual({ a: [1, 2] }, { a: [1, 2] }) // true
// check class instanceG.is(Date, new Date()) // trueG.is(Date, '2024-01-01') // false
const numbers = G.isArrayOf(G.isFiniteNumber)numbers([1, 2, 3]) // truenumbers([1, Number.NaN]) // false
const labels = G.isRecordOf(G.isNonBlankString)labels({ primary: 'Save', secondary: 'Cancel' }) // true