Skip to content

G (Guards)

Runtime type checks that narrow the TypeScript type.

The ten dual guard operations are listed with both public call shapes in the complete dual-call catalogue.

type Guard<A> = Refinement<unknown, A>
isString(x: unknown): x is string
isNumber(x: unknown): x is number
isFiniteNumber(x: unknown): x is number
isNonBlankString(x: unknown): x is string
isBoolean(x: unknown): x is boolean
isBigInt(x: unknown): x is bigint
isSymbol(x: unknown): x is symbol
isFunction(x: unknown): x is Function
isDate(x: unknown): x is Date
isError(x: unknown): x is Error
isPromise(x: unknown): x is Promise<unknown>
isArray(x: unknown): x is unknown[]
isObjectType(x: unknown): x is object
isPlainObject(x: unknown): x is Record<string, unknown>
isArrayOf<A>(value: unknown, guard: Guard<A>): value is A[]
isArrayOf<A>(guard: Guard<A>): Guard<A[]>
isRecordOf<A>(value: unknown, guard: Guard<A>): value is Record<string, 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.

isNil(x: unknown): x is null | undefined
isNotNil<T>(x: T | null | undefined): x is T
isNull(x: unknown): x is null
isUndefined(x: unknown): x is undefined
isNullish(x: unknown): x is null | undefined
isNonNull<T>(x: T | null): x is T
isNonNullish<T>(x: T | null | undefined): x is T
isDefined<T>(x: T | undefined): x is T
isDeepEqual(a: unknown, b: unknown): boolean
isDeepEqual(b: unknown): (a: unknown) => boolean
isShallowEqual(a: unknown, b: unknown): boolean
isShallowEqual(b: unknown): (a: unknown) => boolean
isStrictEqual(a: unknown, b: unknown): boolean
isStrictEqual(b: unknown): (a: unknown) => boolean
isEmpty(x: unknown): boolean
isEmptyish(x: unknown): boolean // null, undefined, or empty
isTruthy(x: unknown): boolean
is<T>(val: unknown, ctor: new (...args: any[]) => T): val is T
is<T>(ctor: new (...args: any[]) => T): (val: unknown) => val is T
propIs<T>(obj: Record<string, unknown>, ctor: new (...args: any[]) => T, prop: string): boolean
propIs<T>(ctor: new (...args: any[]) => T, prop: string): (obj: Record<string, unknown>) => boolean
import { pipe } from '@stopcock/fp'
import * as A from '@stopcock/fp/array'
import * as G from '@stopcock/fp/guard'
// narrow unknown API data
const 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 field
if (G.isString(input) && !G.isEmpty(input)) {
submit(input)
}
// filter mixed arrays in pipe
pipe(
rawValues,
A.filter(G.isNumber), // unknown[] → number[]
A.filter((n) => n > 0),
)
// deep equality check
G.isDeepEqual({ a: [1, 2] }, { a: [1, 2] }) // true
pipe({ a: [1, 2] }, G.isDeepEqual({ a: [1, 2] })) // true
// check class instance
G.is(new Date(), Date) // true: data-first
pipe('2024-01-01', G.is(Date)) // false: data-last
const numbers = G.isArrayOf(G.isFiniteNumber)
numbers([1, 2, 3]) // true
G.isArrayOf([1, Number.NaN], G.isFiniteNumber) // false: data-first
const labels = G.isRecordOf(G.isNonBlankString)
labels({ primary: 'Save', secondary: 'Cancel' }) // true