npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@btsheehy/tikka

v2.1.0

Published

Minimal dependency-free functional programming utilities for TypeScript and JavaScript

Readme

tikka

Tikka is a minimal, dependency-free functional utility library for TypeScript and JavaScript. It works out of the box, is fully typed, tested, and bundled for modern ESM tree-shaking so consumers only pay for what they import.

Why use tikka

  • Minimal API surface focused on practical helpers
  • Zero runtime dependencies
  • TypeScript type declarations published in package output
  • Curried utilities for composable functional style
  • Tested with Vitest
  • Bundled with Rollup and validated for tree-shaking

Why teams switch from larger FP utility libraries

A common pain point with utility libraries is bundle bloat when a project imports a broad FP API surface.

Measured with the same browser bundling setup (esbuild, minified, loading each library's full FP API object):

  • tikka/all: 5.25 KiB minified / 2.29 KiB gzip
  • lodash/fp: 82.27 KiB minified / 29.15 KiB gzip
  • ramda: 68.04 KiB minified / 20.30 KiB gzip

That means tikka/all is roughly:

  • ~15.7x smaller than lodash/fp (minified)
  • ~13.0x smaller than ramda (minified)

If you only import specific named utilities from tikka, tree-shaking can reduce this even further.

Installation

npm install tikka

Usage

Named imports (recommended for tree-shaking):

import { filter, isEven, map, pipe, plus } from 'tikka'

const result = pipe(filter(isEven), map(plus(1)))([1, 2, 3, 4])
// [3, 5]

Default aggregate import (all utilities):

import tikka from 'tikka/all'

const out = tikka.map(tikka.multiply(2), [1, 2, 3]) // [2, 4, 6]

Data-first wrappers (underscore-prefixed):

import { _get, _map, _sortBy } from 'tikka/data-first'

const user = { name: 'Brandon', score: 7 }
const name = _get(user, 'name')
const doubled = _map([1, 2, 3], (n) => n * 2)
const sorted = _sortBy([{ score: 2 }, { score: 1 }], 'score', 'asc')

Tree-shaking

Use named ESM imports from tikka:

import { plus } from 'tikka'

This package is published with sideEffects: false and ESM module output designed for dead-code elimination. Verified with a Rollup consumer bundle: importing only plus emits only the plus implementation.

API

Notes:

  • Many helpers are curried (especially those implemented with curryRight).
  • Argument order for most collection helpers is callback/key first, then data.
  • Aliases are documented explicitly below.

and(a, b)

Boolean AND over two values. Example:

and(true, false) // false

any(test, arr)

Returns true if at least one array element passes test. Example:

any((x) => x > 2, [1, 2, 3]) // true
any(gt(2), [1, 2, 3]) // true

compact(arr)

Removes null and undefined values from an array. Example:

compact([1, null, 2, undefined]) // [1,2]

concat(left, right)

Concatenates arrays or strings. Examples:

concat([1, 2], [3, 4]) // [1,2,3,4]
concat('ab', 'cd') // 'abcd'

contains(value, searchTarget)

Checks whether searchTarget.includes(value) is true. Example:

contains(2, [1, 2, 3]) // true

countBy(iteratee, arr)

Builds a frequency object keyed by iteratee(value). Example:

countBy((x) => (isOdd(x) ? 'odd' : 'even'), [1, 2, 3, 4]) // { odd: 2, even: 2 }

countWhere(test, arr)

Counts array elements matching test. Example:

countWhere((x) => x % 2 === 0, [1, 2, 3, 4]) // 2
countWhere(isEven, [1, 2, 3, 4]) // 2

curry(fn, arity?)

Left-to-right curry helper. Example:

curry((a, b, c) => a + b + c)(1)(2)(3) // 6

curryRight(fn, arity?)

Right-to-left curry helper. Example:

curryRight((a, b) => a / b)(2, 8) // 4

debug(msg, value)

Logs msg and value, then returns value. Example:

debug('current', 42) // 42

This is most useful for debugging a particularly long pipe command.

deepClone(data)

Deep-clones arrays/objects recursively (supports Date and RegExp cloning). Example:

deepClone({ a: { b: [1] } })

deepForEach(func, data)

Recursively visits nested arrays/objects and runs func on leaf values. Example:

deepForEach(console.log, { a: [1, { b: 2 }] })
// 1
// 2

deepMap(func, data)

Recursively maps leaf values in nested arrays/objects. Example:

deepMap((x) => (typeof x === 'number' ? x * 2 : x), { a: [1, 2, 'c'] })
// { a: [2, 4, 'c'] }

every(test, arr)

Returns true if all elements pass test. Example:

every((x) => x > 0, [1, 2, 3]) // true
every(lt(0))([-4, -5, 2]) // false

filter(test, arr)

Filters an array by predicate. Example:

filter((x) => x > 1, [1, 2, 3]) // [ 2,3 ]
filter(gt(1), [1, 2, 3]) // [2, 3]

find(test, arr)

Returns first matching element or undefined. Example:

find((x) => x > 1, [1, 2, 3]) // 2
find(gt(1), [1, 2, 3]) // 2

findIndex(test, arr)

Returns index of first matching element or -1. Example:

findIndex(gt(1), [1, 2, 3]) // 1

first(arr)

Alias of head. Example:

first([1, 2, 3]) // 1

flatten(arr)

Deep-flattens nested arrays. Example:

flatten([1, [2, [3]], 4]) // [1,2,3,4]

forEach(func, arr)

Runs func for each element and returns original array. Example:

forEach(console.log, [1, 2])
// 1
// 2

forEachValues(func, obj)

Runs func for each object value and returns original object. Example:

forEachValues(console.log, { a: 1, b: 2 })
// 1
// 2

get(prop, obj)

Gets property by key. Example:

get('a', { a: 1 }) // 1

getOr(defaultValue, prop, obj)

Gets property if present, otherwise returns defaultValue. Example:

getOr(0, 'a', {}) // 0

grab(props, data)

Picks listed keys from an object, or from each object in an array. Example:

grab(['a'], { a: 1, b: 2 }) // { a: 1 }
grab(['a'], [{ a: 1, b: 2 }, { a: 5, c: 9 }]) // [{ a: 1 }, { a: 5 }]

groupBy(groupingFunction, arr)

Groups array values by iteratee or string key. Example:

groupBy((x) => isOdd(x) ? 'odd' : 'even', [1, 2, 3]) // { odd: [ 1, 3 ], even: [ 2 ] }
groupBy('foo', [{ foo: 1, bar: 300, baz: 'abc'}, { foo: 2, bar: 9, baz: 'def'}, { foo: 2, bar: 1000 }]) 
// {
//   '1': [ { foo: 1, bar: 300, baz: 'abc' } ],
//   '2': [ { foo: 2, bar: 9, baz: 'def' }, { foo: 2, bar: 1000 } ]
// }

gt(a, b)

Greater-than comparison. Example:

gt(3, 2) // true

gte(a, b)

Greater-than-or-equal comparison. Example:

gte(3, 3) // true

has(prop, obj)

Own-property existence check. Example:

has('a', { a: 1 }) // true

head(arr)

Returns first element. Example:

head([1, 2, 3]) // 1

highest(nums)

Returns the largest number in an array. Example:

highest([4, 1, 9, 3]) // 9

highestBy(fn, arr)

Returns the element with the highest score from fn. Example:

highestBy((user) => user.score, [{ name: 'Ari', score: 12 }, { name: 'Bea', score: 21 }])
// { name: 'Bea', score: 21 }

identity(value)

Alias of self; returns input unchanged. Example:

identity('x') // 'x'

ifElse(onFalse, onTrue, test)

Runs test(), then executes onTrue() or onFalse(). Example:

ifElse(
  () => 0,
  () => 1,
  () => true
) // 1

includes(value, searchTarget)

Alias of contains. Example:

includes('a', 'cat') // true

isEven(num)

Returns true for even numbers. Example:

isEven(4) // true

isOdd(num)

Returns true for odd numbers. Example:

isOdd(3) // true

last(arrOrString)

Returns final element/character. Example:

last([1, 2, 3]) // 3

lowest(nums)

Returns the smallest number in an array. Example:

lowest([4, 1, 9, 3]) // 1

lowestBy(fn, arr)

Returns the element with the lowest score from fn. Example:

lowestBy((user) => user.score, [{ name: 'Ari', score: 12 }, { name: 'Bea', score: 21 }])
// { name: 'Ari', score: 12 }

lt(a, b)

Less-than comparison. Example:

lt(2, 3) // true

lte(a, b)

Less-than-or-equal comparison. Example:

lte(3, 3) // true

map(fn, arr)

Maps array values. Example:

map((x) => x * 2, [1, 2, 3]) // [2,4,6]

mapKeys(fn, obj)

Transforms object keys, preserving values. Example:

mapKeys((k) => k.toUpperCase(), { a: 1 }) // { A: 1 }

mapValues(fn, obj)

Transforms object values, preserving keys. Example:

mapValues((v) => v * 2, { a: 1 }) // { a: 2 }

max(a, b)

Returns the larger of two numbers. Example:

max(4, 9) // 9

min(a, b)

Returns the smaller of two numbers. Example:

min(4, 9) // 4

minus(b, a)

Subtracts second arg from first in curried-right style. Example:

minus(3, 10) // 7

noop()

No-op function. Example:

noop() // undefined

or(a, b)

Boolean OR over two values. Example:

or(false, true) // true

pipe(...fns)

Creates a left-to-right function pipeline. This is the core composition primitive in tikka.

Example:

import { pipe } from 'tikka'

const transform = pipe(
  (value: number) => value + 1,
  (value) => value * 2
)

transform(2)
// 6

Common tikka composition pattern:

import { filter, isEven, map, pipe, plus, take } from 'tikka'

const firstThreeIncrementedEvens = pipe(filter(isEven), map(plus(1)), take(3))

firstThreeIncrementedEvens([1, 2, 3, 4, 5, 6, 7, 8])
// [3, 5, 7]

Pipeline with object helpers:

import { grab, map, pipe, sortBy } from '@btsheehy/tikka'

const selectLeaderboardFields = pipe(sortBy('score', 'desc'), map(grab(['name', 'score'])))

selectLeaderboardFields([
  { name: 'Ari', score: 12, email: '[email protected]' },
  { name: 'Bea', score: 19, email: '[email protected]' },
])
// [{ name: 'Bea', score: 19 }, { name: 'Ari', score: 12 }]

pluck(prop, collection)

Extracts property values from array of objects. Example:

pluck('name', [{ name: 'a' }, { name: 'b' }]) // ['a','b']

plus(a, b)

Adds two numbers. Example:

plus(2, 3) // 5

remove(pred, arr)

Returns a new array with items removed when pred(item) is true. Example:

remove((n) => n % 2 === 0, [1, 2, 3, 4]) // [1, 3]

select(props, data)

Alias of grab. Example:

select(['id'], { id: 1, name: 'x' }) // { id:1 }

self(value)

Returns input unchanged. Example:

self(42) // 42

sort(iteratee, arr)

Sorts an array by a computed key (non-mutating). Example:

sort((x) => x.age, [{ age: 3 }, { age: 1 }]) // [{age:1},{age:3}]

sortBy(fieldOrIteratee, direction, arr)

Sorts an array of objects by either a field name or a value-selector function, using 'asc' or 'desc' direction. Examples:

sortBy('age', 'desc', [{ age: 1 }, { age: 3 }, { age: 2 }]) // [{age:3},{age:2},{age:1}]
sortBy((x) => x.age, 'asc', [{ age: 3 }, { age: 1 }]) // [{age:1},{age:3}]

tail(arrOrString)

Returns everything except the first element/character. Examples:

tail([1, 2, 3]) // [2,3]
tail('hello') // 'ello'

take(num, arr)

Returns first num elements. Example:

take(2, [1, 2, 3]) // [1,2]

test(regex, str)

Runs regex.test(str). Example:

test(/ab/, 'zabx') // true

toLower(str)

Lowercases string with locale support. Example:

toLower('AbC') // 'abc'

toUpper(str)

Uppercases string with locale support. Example:

toUpper('AbC') // 'ABC'

trim(str)

Trims string whitespace. Example:

trim('  x  ') // 'x'

type(val)

Returns internal type label (for example: Array, Object, Null, Undefined). Example:

type([]) // 'Array'

uniq(arr)

Returns array with duplicate values removed (first occurrence kept). Example:

uniq([1, 2, 1, 3]) // [1,2,3]

uniqBy(uniqCond, arr)

Returns unique items by computed key. Example:

uniqBy(get('id'), [{ id: 1 }, { id: 1 }, { id: 2 }])

Performance benchmark suites (large datasets)

This repo includes dedicated performance benchmark suites to compare identical function workloads across tikka, lodash/fp, and ramda on very large deterministic datasets.

Benchmarked functions:

  • map
  • filter
  • find
  • groupBy
  • uniq
  • uniqBy
  • flatten / flattenDeep
  • contains / includes
  • Composed pipe collection pipeline (filter -> map -> uniq -> find)

Dataset sizes (generated in test/perf/fixtures.ts):

  • numbers: 60,000 items
  • users: 80,000 objects
  • nestedNumbers: 5,000 nested groups

Commands:

  • Parity validation (same output across all three libs): vitest run test/perf/parity.test.ts
  • Performance suite: npm run perf
  • Watch performance suite: npm run perf:watch
  • Run a specific benchmark group by test name pattern: npm run perf:test -- map
  • Run the composed pipeline benchmark group: npm run perf:test -- "pipe collection pipeline"

Quality gates in this repo

  • Lint: npm run lint
  • Type-check: npm run typecheck
  • Tests: npm test
  • Build: npm run build
  • Full check: npm run check

Biome rule policy

This codebase intentionally allows dynamic typing patterns used by curried functional helpers:

  • suspicious.noExplicitAny: off
  • complexity.noBannedTypes: off

Those two rules were disabled to avoid unsafe blanket suppressions in many files while preserving the rest of Biome recommended rules.