Pure, curried, functional programming utilities for TypeScript.
Inspired by Ramda and Sanctuary
npm install fpureimport { compose, add, map, filter, gt } from 'fpure';
compose(
filter(gt(3)),
map(add(1))
)([1, 2, 3, 4]) // => [4, 5] (add 1 to each, then keep values > 3)| Metric | fpure |
Ramda | Sanctuary |
|---|---|---|---|
| Bundle size (gzip) | ~12 KB (all 281 funcs, unminified) | ~14 KB (min+gzip) | ~30 KB (min+gzip) |
| Zero runtime dependencies | Yes | Yes | No (type system deps) |
| Runtime type checks | No (types at compile time only) | No | Yes (runtime validation) |
| TypeScript declarations | Built-in, ships with package | Requires @types/ramda |
Requires @types/sanctuary |
| Deep import for tree-shaking (CJS) | Supported (require('fpure/list/map')) |
Supported (require('ramda/src/map')) |
N/A (single module) |
| Deep import for tree-shaking (ESM) | Supported (import { map } from 'fpure/list/map') |
Supported (import map from 'ramda/es/map') |
N/A (single module) |
| Per-function internal dependencies | 1 (curry) for 269/281; a few reuse 1–3 helpers (equals, lenses, mean) |
2–8 (_curry2, _dispatchable, _xmap, etc.) |
Varies |
Data-last sub(3)(10) |
7 (10 − 3) |
−7 (3 − 10) |
7 |
Data-last gt(3)(5) |
true (5 > 3) |
false (3 > 5) |
true |
| Function count | 281 | ~272 | ~165 |
Ramda and Sanctuary figures are approximate, measured against their current releases (Ramda 0.32, Sanctuary 3.1).
fpurefigures are measured directly from this package.
Data-last for ALL binary ops. Ramda's sub, div, mod, and gt/lt/gte/lte use config-first ordering that produces surprising results (sub(3)(10) = -7, gt(3)(5) = false). fpure is consistently data-last: sub(3)(10) = 7, gt(3)(5) = true.
Minimal per-function overhead. Each Ramda function imports 2–8 internal helpers (_curry2, _dispatchable, _xfilter, etc.). 269 of 281 fpure functions import only curry; a handful reuse a nearby helper instead (e.g. mean uses sum, the lens constructors use lens).
CJS & ESM tree-shakeable. fpure ships every function as a standalone file — CommonJS for Node, ESM for bundlers — each with a matching .d.ts. You can deep-import require('fpure/list/map') or import { map } from 'fpure/list/map' and get exactly that function + curry: no barrel overhead, no bundled internals, and the CJS form works in Node without a bundler. A bundled ESM deep import was measured at ~0.4 KB vs ~27 KB for the full lib. Ramda's CJS deep imports point at unbuilt source (ramda/src/map), and require('ramda') pulls in the whole library either way (a single ~351 KB bundle in versions ≤ 0.28, a full src/ barrel in newer ones).
Zero runtime type overhead. Sanctuary validates types at runtime. fpure uses TypeScript types only, meaning zero runtime cost, faster execution, smaller bundles.
Wraps a function so partial application returns a curried function until all args are satisfied.
const add = curry((a: number, b: number) => a + b)
add(1, 2) // => 3
add(1)(2) // => 3
const inc = add(1)
inc(5) // => 6Right-to-left function composition.
compose(x => x + 1, x => x * 2)(5) // => 11 (5*2 + 1)Left-to-right function composition.
pipe(x => x + 1, x => x * 2)(5) // => 12 ((5+1)*2)identity(5) // => 5
identity({a:1}) // => {a:1}Creates a function that always returns the given value.
const f = always(42)
f() // => 42
f(1, 2, 3) // => 42Always returns true.
Always returns false.
Swaps the first two arguments of a function.
const sub = curry((a, b) => a - b)
flip(sub, 3, 10) // => -7 (calls sub(10, 3) = 3 − 10)Applies a function to an argument.
apply(x => x + 1, 5) // => 6Runs a side-effect function on a value, returns the value.
tap(console.log, 42) // logs 42, returns 42Applies a list of functions to the same arguments, returns results.
juxt([x => x + 1, x => x * 2])(5) // => [6, 10]Wraps a function so it only executes once. Subsequent calls return the cached result.
Wraps a function so results are cached by argument identity (JSON-serialized).
Accepts a converging function and a list of branches. Returns a function that applies branches to its args, then feeds results to the converger.
converge((a, b) => a + b, [x => x * 2, x => x * 3])(5) // => 25 (10 + 15)Data-first apply: applies a value to a function.
applyTo(5, x => x + 1) // => 6Wraps a try/catch as a function.
tryCatch(x => { if (x < 0) throw Error(); return x; }, () => 0, -1) // => 0Partially applies arguments from the left.
const add3 = (a, b, c) => a + b + c
partial(add3, 1)(2, 3) // => 6Partially applies arguments from the right.
const greet = (s, t, n) => `${s}, ${t} ${n}`
partialRight(greet, 'Ms.', 'Smith')('Hello') // => 'Hello, Ms. Smith'Curries a function to a specific arity.
curryN(3, (a, b, c) => a + b + c)(1)(2)(3) // => 6Creates comparator functions from a transform for sorting.
const byLength = ascend(x => x.length)
['aaa', 'b', 'cc'].sort(byLength) // => ['b', 'cc', 'aaa']Creates a comparator from a simple predicate.
const byFirst = comparator((a, b) => a < b)
[3, 1, 2].sort(byFirst) // => [1, 2, 3]Compose/pipe with a custom sequencing function.
Left-to-right async composition (pipe for promises).
Lifts a binary function over arrays (cartesian product).
lift((a, b) => a + b, [1, 2], [3, 4]) // => [4, 5, 5, 6]Memoize with a custom key function.
Promise error handler (.catch as a function).
Right-to-left composition of two unary functions.
o(x => x * 2, x => x + 1)(5) // => 12Applies a binary function to two transformed values.
on((a, b) => a + b, x => x * 2, 5, 3) // => 16bind, call, invoker, nAry, nthArg, of, unapply, unary, uncurryN, useWith, ascendNatural, descendNatural, promap, thunkify, liftN
Returns true when both predicates pass for a value.
const isPositive = x => x > 0
const isEven = x => x % 2 === 0
both(isPositive, isEven, 4) // => true
both(isPositive, isEven, 3) // => falseReturns true when either predicate passes.
either(isPositive, isEven, 4) // => true
either(isPositive, isEven, -3) // => falseReturns the first falsy argument, otherwise the last argument.
and(null, 42) // => null
and(true, 42) // => 42Returns the first truthy argument, otherwise the last argument.
or(42, 100) // => 42
or(null, 42) // => 42Logical negation.
not(true) // => false
not(false) // => trueNegates a predicate function.
const isOdd = complement(x => x % 2 === 0)
isOdd(1) // => trueConditional branching as a function.
ifElse(x => x > 0, x => x + 1, x => x - 1, 5) // => 6
ifElse(x => x > 0, x => x + 1, x => x - 1, -5) // => -6Applies a function when the predicate is true, otherwise returns the value unchanged.
when(x => x > 0, x => x * 2, 5) // => 10
when(x => x > 0, x => x * 2, -5) // => -5Applies a function when the predicate is false, otherwise returns the value unchanged.
unless(x => x > 0, x => x * 2, -5) // => -10
unless(x => x > 0, x => x * 2, 5) // => 5Pattern matching via predicate/transformer pairs.
const classify = cond([
[x => x < 0, () => 'negative'],
[x => x === 0, () => 'zero'],
[x => x > 0, () => 'positive'],
])
classify(-1) // => 'negative'
classify(0) // => 'zero'
classify(1) // => 'positive'Returns true when all predicates in a list pass.
allPass([x => x > 0, x => x % 2 === 0], 4) // => true
allPass([x => x > 0, x => x % 2 === 0], 3) // => falseReturns true when any predicate in a list passes.
anyPass([x => x > 0, x => x % 2 === 0], 4) // => true
anyPass([x => x > 0, x => x % 2 === 0], -3) // => falseReturns the default value when the input is null or undefined.
defaultTo(42, null) // => 42
defaultTo(42, undefined) // => 42
defaultTo(42, 0) // => 0Returns true for null, undefined, empty string, empty array, or empty object.
isEmpty(null) // => true
isEmpty([]) // => true
isEmpty({}) // => true
isEmpty('') // => true
isEmpty('a') // => falseNegation of isEmpty.
isNotEmpty('a') // => true
isNotEmpty(null) // => falseChecks if a value at a path satisfies a predicate.
pathSatisfies(x => x > 0, ['a', 'b'], {a: {b: 5}}) // => trueChecks if a property satisfies a predicate.
propSatisfies(x => x > 0, 'a', {a: 5}) // => trueApplies a function to a value until the predicate is satisfied.
until(x => x >= 100, x => x * 2, 1) // => 128Exclusive OR. True when exactly one argument is truthy.
xor(true, false) // => true
xor(false, true) // => true
xor(true, true) // => false
xor(false, false) // => falseAll binary math ops use data-last: op(config, value) = value op config.
add(4, 5) // => 9
add(4)(5) // => 9sub(3, 10) // => 7 (10 - 3)
sub(3)(10) // => 7mul(4, 5) // => 20div(2, 10) // => 5 (10 / 2)
div(2)(10) // => 5mod(3, 10) // => 1 (10 % 3)True modulo (not remainder). Only works with positive integer modulus.
mathMod(5, 17) // => 2
mathMod(5, -17) // => 3
mathMod(5, 17.2) // => NaN (non-integer)
mathMod(-5, 17) // => NaN (non-positive modulus)inc(5) // => 6dec(5) // => 4negate(5) // => -5sum([1, 2, 3, 4]) // => 10
sum([]) // => 0product([1, 2, 3, 4]) // => 24
product([]) // => 1mean([1, 2, 3, 4]) // => 2.5median([1, 2, 3, 4, 5]) // => 3
median([1, 2, 3, 4]) // => 2.5min(3, 5) // => 3max(3, 5) // => 5clamp(1, 10, 5) // => 5
clamp(1, 10, 0) // => 1
clamp(1, 10, 15) // => 10const byLength = x => x.length
minBy(byLength, 'short', 'longer') // => 'short'maxBy(byLength, 'short', 'longer') // => 'longer'All binary comparisons use data-last: cmp(threshold, value) = value > threshold.
Greater than.
gt(3, 5) // => true (5 > 3)
gt(5, 3) // => false
gt(3)(5) // => true "is arg > 3?"
const gt5 = gt(5)
gt5(10) // => true
gt5(3) // => falseLess than.
lt(5, 3) // => true (3 < 5)
lt(3, 5) // => falseGreater than or equal.
gte(3, 5) // => true
gte(3, 3) // => true
gte(5, 3) // => falseLess than or equal.
lte(5, 3) // => true
lte(3, 3) // => true
lte(3, 5) // => falseSame-value-zero equality (like Object.is).
identical(1, 1) // => true
identical(1, '1') // => false
identical(NaN, NaN) // => true
identical(0, -0) // => false
identical([], []) // => falseDeep equality. Handles arrays, nested objects, Dates, RegExps, Maps, Sets, circular references.
equals([1, [2, 3]], [1, [2, 3]]) // => true
equals({a: 1, b: {c: 3}}, {a: 1, b: {c: 3}}) // => true
equals(new Date(0), new Date(0)) // => trueChecks if a property equals a value.
propEq(42, 'a', {a: 42}) // => true
propEq(99, 'a', {a: 42}) // => falseChecks if a value at a path equals a target.
pathEq(42, ['a', 'b'], {a: {b: 42}}) // => trueCompares two values after applying a transform.
eqBy(x => x.length, 'hello', 'world') // => trueSorts with multiple comparators (priority order).
const data = [{name: 'a', age: 30}, {name: 'b', age: 20}]
sortWith([(a, b) => a.name.localeCompare(b.name), (a, b) => a.age - b.age], data)Relationship operations with custom comparators.
Data-last: the list is always the last argument.
map(x => x * 2, [1, 2, 3]) // => [2, 4, 6]filter(x => x > 1, [1, 2, 3]) // => [2, 3]reject(x => x > 1, [1, 2, 3]) // => [1]reduce((a, b) => a + b, 0, [1, 2, 3]) // => 6reduceRight((a, b) => a + b, 0, [1, 2, 3]) // => 6find(x => x > 1, [1, 2, 3]) // => 2
find(x => x > 10, [1, 2, 3]) // => undefinedfindIndex(x => x > 1, [1, 2, 3]) // => 1findLast(x => x < 3, [1, 2, 3, 2, 1]) // => 1 (last match)findLastIndex(x => x < 3, [1, 2, 3, 2, 1]) // => 4head([1, 2, 3]) // => 1
head([]) // => undefinedtail([1, 2, 3]) // => [2, 3]last([1, 2, 3]) // => 3init([1, 2, 3]) // => [1, 2]take(2, [1, 2, 3, 4]) // => [1, 2]drop(2, [1, 2, 3, 4]) // => [3, 4]takeWhile(x => x < 3, [1, 2, 3, 4]) // => [1, 2]dropWhile(x => x < 3, [1, 2, 3, 4]) // => [3, 4]takeLast(2, [1, 2, 3, 4]) // => [3, 4]dropLast(2, [1, 2, 3, 4]) // => [1, 2]slice(1, 3, [1, 2, 3, 4]) // => [2, 3]splitAt(2, [1, 2, 3, 4]) // => [[1, 2], [3, 4]]splitEvery(2, [1, 2, 3, 4, 5]) // => [[1, 2], [3, 4], [5]]Removes count elements starting at start.
remove(1, 2, [1, 2, 3, 4]) // => [1, 4]Inserts an element at the given index.
insert(1, 99, [1, 2, 3]) // => [1, 99, 2, 3]Inserts multiple elements at the given index.
insertAll(1, [99, 100], [1, 2, 3]) // => [1, 99, 100, 2, 3]Replaces the element at the given index.
update(1, 99, [1, 2, 3]) // => [1, 99, 3]Applies a function to the element at the given index.
adjust(x => x * 10, 1, [1, 2, 3]) // => [1, 20, 3]append(4, [1, 2, 3]) // => [1, 2, 3, 4]prepend(0, [1, 2, 3]) // => [0, 1, 2, 3]concat([1, 2], [3, 4]) // => [1, 2, 3, 4]Recursively flattens nested arrays.
flatten([1, [2, [3]], 4]) // => [1, 2, 3, 4]Maps a function over a list and flattens (flatMap).
chain(x => [x, x * 2], [1, 2, 3]) // => [1, 2, 2, 4, 3, 6]Inserts a separator between elements.
interpose(', ', [1, 2, 3]) // => [1, ', ', 2, ', ', 3]Inserts a separator list between sublists and flattens.
intercalate([', '], [[1], [2], [3]]) // => [1, ', ', 2, ', ', 3]repeat(5, 3) // => [5, 5, 5]Calls a function n times with the index.
times(x => x * 2, 5) // => [0, 2, 4, 6, 8]zip([1, 2, 3], ['a', 'b', 'c']) // => [[1, 'a'], [2, 'b'], [3, 'c']]
zip([1, 2, 3], ['a', 'b']) // => [[1, 'a'], [2, 'b']]zipObj(['a', 'b', 'c'], [1, 2, 3]) // => {a: 1, b: 2, c: 3}zipWith((a, b) => a + b, [1, 2, 3], [10, 20, 30]) // => [11, 22, 33]Groups list elements by a key function.
groupBy(x => x > 2 ? 'big' : 'small', [1, 2, 3, 4])
// => { small: [1, 2], big: [3, 4] }Groups consecutive equal elements.
groupWith((a, b) => a === b, [1, 1, 2, 2, 3])
// => [[1, 1], [2, 2], [3]]Creates a sorted copy (mutates nothing).
sort((a, b) => a - b, [3, 1, 2]) // => [1, 2, 3]Sorts by a transform function.
sortBy(x => x.length, ['aaa', 'b', 'cc']) // => ['b', 'cc', 'aaa']Ascending numeric sort.
asc([3, 1, 2]) // => [1, 2, 3]Descending numeric sort.
desc([1, 2, 3]) // => [3, 2, 1]all(x => x > 0, [1, 2, 3]) // => true
all(x => x > 1, [1, 2, 3]) // => falseany(x => x > 2, [1, 2, 3]) // => true
any(x => x > 10, [1, 2, 3]) // => falsenone(x => x > 10, [1, 2, 3]) // => true
none(x => x > 1, [1, 2, 3]) // => falseincludes(2, [1, 2, 3]) // => true
includes(4, [1, 2, 3]) // => falseRemoves specified values.
without([1, 2], [1, 2, 1, 3, 2, 4]) // => [3, 4]Elements in the second list not in the first.
difference([1, 2, 3], [2, 3, 4]) // => [4]Elements in both lists.
intersection([1, 2, 3], [2, 3, 4]) // => [2, 3]Unique elements from both lists.
union([1, 2, 3], [2, 3, 4]) // => [1, 2, 3, 4]uniq([1, 2, 1, 3, 2]) // => [1, 2, 3]Deduplicate by a key function.
uniqBy(x => x % 2, [1, 2, 3, 4, 5]) // => [1, 2]Deduplicate with a custom comparator.
uniqWith((a, b) => a % 2 === b % 2, [1, 2, 3, 4, 5]) // => [1, 2]Returns overlapping subsequences of length n.
aperture(2, [1, 2, 3, 4]) // => [[1, 2], [2, 3], [3, 4]]
aperture(3, [1, 2, 3, 4]) // => [[1, 2, 3], [2, 3, 4]]Splits into [pass, fail] arrays.
partition(x => x % 2 === 0, [1, 2, 3, 4, 5])
// => [[2, 4], [1, 3, 5]]Splits into [takeWhile, rest].
span(x => x < 3, [1, 2, 3, 4, 1]) // => [[1, 2], [3, 4, 1]]Extracts a key from each object in a list.
pluck('a', [{a: 1}, {a: 2}]) // => [1, 2]Selects specific keys from a list of objects.
project(['a', 'b'], [{a: 1, b: 2, c: 3}, {a: 4, b: 5, c: 6}])
// => [{a: 1, b: 2}, {a: 4, b: 5}]fromPairs([['a', 1], ['b', 2]]) // => {a: 1, b: 2}toPairs({a: 1, b: 2}) // => [['a', 1], ['b', 2]]reverse([1, 2, 3]) // => [3, 2, 1]length([1, 2, 3]) // => 3Counts occurrences by a key function.
countBy(x => x > 2 ? 'big' : 'small', [1, 2, 3, 4])
// => { small: 2, big: 2 }Runs a side effect for each element, returns the original array.
forEach(x => console.log(x), [1, 2, 3])
// logs 1, 2, 3 and returns [1, 2, 3]Indexes a list by a key function. Last duplicate wins.
indexBy(x => x.id, [{id: 'a', v: 1}, {id: 'b', v: 2}])
// => { a: {id: 'a', v: 1}, b: {id: 'b', v: 2} }Generates a range of numbers.
range(1, 5) // => [1, 2, 3, 4]Returns the nth element (supports negative indices).
nth(2, [1, 2, 3, 4]) // => 3
nth(-1, [1, 2, 3]) // => 3Transposes a list of lists (matrix flip).
transpose([[1, 2], [3, 4]]) // => [[1, 3], [2, 4]]Cartesian product of two arrays.
xprod([1, 2], ['a', 'b']) // => [[1, 'a'], [1, 'b'], [2, 'a'], [2, 'b']]Removes consecutive duplicate elements.
dropRepeats([1, 1, 2, 2, 3]) // => [1, 2, 3]Moves an element from one index to another.
move(0, 2, [1, 2, 3]) // => [2, 3, 1]Swaps two elements by index.
swap(0, 2, [1, 2, 3]) // => [3, 2, 1]Flattens one level.
unnest([[1], [2, 3], [[4]]]) // => [1, 2, 3, [4]]Like reduce but yields all intermediate accumulators.
scan((a, b) => a + b, 0, [1, 2, 3]) // => [0, 1, 3, 6]Split arrays when/wherever a predicate matches.
Take/drop from the end while a predicate holds.
Group/reduce by a key function.
Accumulate while mapping (like reduce that produces an array).
Find the index of a value.
indexOf(2, [1, 2, 3]) // => 1Generates a list from a seed value.
unfold(x => x < 5 ? [x, x + 1] : false, 1) // => [1, 2, 3, 4]Transduce with a transducer; traverse with an applicative.
Reduce with early termination.
dropLastWhile, dropRepeatsBy, dropRepeatsWith, pair, reduceBy, splitWhenever, swap
keys({a: 1, b: 2}) // => ['a', 'b']values({a: 1, b: 2}) // => [1, 2]entries({a: 1, b: 2}) // => [['a', 1], ['b', 2]]Own-property check.
has('a', {a: 1}) // => true
has('toString', {}) // => falseSafely access nested paths.
path(['a', 'b'], {a: {b: 42}}) // => 42
path(['a', 'c'], {a: {b: 42}}) // => undefinedpathOr(99, ['a', 'c'], {a: {b: 42}}) // => 99prop('a', {a: 42}) // => 42propOr(99, 'b', {a: 42}) // => 99props(['a', 'b'], {a: 1, b: 2, c: 3}) // => [1, 2]assoc('b', 2, {a: 1}) // => {a: 1, b: 2}
assoc('a', 2, {a: 1}) // => {a: 2}assocPath(['a', 'b'], 42, {}) // => {a: {b: 42}}dissoc('a', {a: 1, b: 2, c: 3}) // => {b: 2, c: 3}dissocPath(['a', 'b'], {a: {b: 1, c: 2}, d: 3})
// => {a: {c: 2}, d: 3}merge({a: 1}, {b: 2}) // => {a: 1, b: 2}mergeAll([{a: 1}, {b: 2}, {c: 3}]) // => {a: 1, b: 2, c: 3}Left-biased merge (left wins).
mergeLeft({a: 1}, {a: 2}) // => {a: 1}Right-biased merge (right wins).
mergeRight({a: 1}, {a: 2}) // => {a: 2}Deep recursive merge.
mergeDeepRight({a: {b: 1}}, {a: {c: 2}})
// => {a: {b: 1, c: 2}}pick(['a', 'c'], {a: 1, b: 2, c: 3}) // => {a: 1, c: 3}pickBy(v => typeof v === 'number', {a: 1, b: 'x', c: 3})
// => {a: 1, c: 3}omit(['a', 'c'], {a: 1, b: 2, c: 3}) // => {b: 2}omitBy(v => typeof v === 'number', {a: 1, b: 'x', c: 3})
// => {b: 'x'}Tests if an object matches a predicate spec.
where({a: x => x > 0, b: x => x.length > 1}, {a: 5, b: 'hi'})
// => trueTests if an object matches a value spec.
whereEq({a: 1, b: 'hi'}, {a: 1, b: 'hi', c: 3})
// => trueRecursively transforms an object's values by a spec.
evolve({a: x => x * 2, b: x => x + 1}, {a: 5, b: 10, c: 99})
// => {a: 10, b: 11, c: 99}Creates a function that applies args to a spec of functions.
const spec = applySpec({sum: (...xs) => xs.reduce((a,b) => a+b), max: Math.max})
spec(1, 2, 3) // => {sum: 6, max: 3}Lenses provide focused access and immutability on nested data.
const l = lensProp('a')
view(l, {a: 1, b: 2}) // => 1
set(l, 42, {a: 1, b: 2}) // => {a: 42, b: 2}
over(l, x => x + 1, {a: 1, b: 2}) // => {a: 2, b: 2}const l = lensPath(['a', 'b', 'c'])
view(l, {a: {b: {c: 42}}}) // => 42
set(l, 99, {a: {b: {c: 42}}}) // => {a: {b: {c: 99}}}const l = lensIndex(1)
view(l, [10, 20, 30]) // => 20
set(l, 99, [10, 20, 30]) // => [10, 99, 30]
over(l, x => x + 5, [10, 20, 30]) // => [10, 25, 30]Extracts the focused value.
Sets the focused value (returns new object, original unchanged).
Applies a function to the focused value.
Deep clones a value (JSON roundtrip).
clone({a: 1, b: {c: 2}}) // => {a: 1, b: {c: 2}} (new reference)Applies a function to a property or path.
modify('a', x => x * 2, {a: 5, b: 10}) // => {a: 10, b: 10}
modifyPath(['a', 'b'], x => x * 2, {a: {b: 5}}) // => {a: {b: 10}}Creates a single-key object.
objOf('a', 1) // => {a: 1}Like pick but includes undefined for missing keys.
Renames keys using a mapping.
renameKeys({a: 'x', b: 'y'}, {a: 1, b: 2}) // => {x: 1, y: 2}Returns true if any predicate in the spec matches.
whereAny({a: x => x > 0, b: x => x.length > 1}, {a: 0, b: 'hi'}) // => trueDeep merge with left values taking precedence.
Custom merge strategies.
eqProps, forEachObjIndexed, hasIn, hasPath, invert, invertObj, keysIn, mapKeys, mapObjIndexed, paths, toPairsIn, unwind, valuesIn
toUpper('hello') // => 'HELLO'toLower('HELLO') // => 'hello'trim(' hello ') // => 'hello'split(',', 'a,b,c') // => ['a', 'b', 'c']join(',', ['a', 'b', 'c']) // => 'a,b,c'replace('world', 'there', 'hello world') // => 'hello there'
replace(/o/g, '0', 'hello') // => 'hell0'test(/hello/, 'hello world') // => truematch(/h(\w)/, 'hello') // => ['he', 'e'] (RegExpMatchArray)startsWith('hello', 'hello world') // => trueendsWith('world', 'hello world') // => truepadStart(5, '0', '42') // => '00042'padEnd(5, '0', '42') // => '42000'toString(42) // => '42'
toString(null) // => 'null'
toString([1,2]) // => '1,2'Returns the internal [[Class]] name.
type({}) // => 'Object'
type([]) // => 'Array'
type(null) // => 'Null'
type(undefined) // => 'Undefined'
type(() => {}) // => 'Function'
type(/[A-Z]/) // => 'RegExp'Checks if a value is an instance of a constructor.
is(Array, []) // => true
is(RegExp, /x/) // => trueisNil(null) // => true
isNil(undefined) // => true
isNil(0) // => falseisNotNil(0) // => true
isNotNil(null) // => falseisArray([]) // => true
isArray({}) // => falsePlain object check (not array, not null).
isObject({}) // => true
isObject([]) // => false
isObject(null) // => falseisString('hello') // => true
isString(42) // => falseExcludes NaN.
isNumber(42) // => true
isNumber(NaN) // => falseisBoolean(true) // => true
isBoolean(false) // => true
isBoolean(0) // => falseisFunction(() => {}) // => trueisDate(new Date()) // => trueisRegExp(/test/) // => trueisError(new Error()) // => trueDetects thenables.
isPromise(Promise.resolve()) // => true
isPromise({then: () => {}}) // => trueisInteger(42) // => true
isInteger(3.14) // => falseisFiniteNum(42) // => true
isFiniteNum(Infinity) // => false
isFiniteNum(NaN) // => falseisNaNVal(NaN) // => true
isNaNVal(42) // => falseAll binary operations follow data-last, config-first: the thing you're likely to partially apply comes first, the data comes last.
| Function | Call | Meaning | Ramda (for comparison) |
|---|---|---|---|
sub(3)(10) |
10 - 3 = 7 |
subtract 3 from 10 | R.subtract(3,10) = -7 |
div(2)(10) |
10 / 2 = 5 |
divide 10 by 2 | R.divide(2,10) = 0.2 |
gt(3)(5) |
5 > 3 = true |
is arg > 3? | R.gt(3,5) = false |
mod(3)(10) |
10 % 3 = 1 |
10 mod 3 | R.mod(3,10) = 3 |
Every function returns a new value. Inputs are never mutated.
No side effects in logic functions. tap and forEach are the only exceptions since they're explicitly for side effects.
Unlike Ramda, fpure never inspects argument types to guess your intent. TypeScript handles type safety at compile time. Runtime is strict.
Clone the repo and make changes to lib/. TypeScript is compiled to dist/ before running tests.
npm install # install dev dependencies (TypeScript, c8)
npm test # compiles lib/ → dist/, then runs tests