DEV Community

Marcin Dziewulski
Marcin Dziewulski

Posted on • Updated on

TS Belt - fast, modern, and practical utility library for FP in TypeScript

ts-belt - fast, modern, and practical utility library for FP in TypeScript

Repo: https://github.com/mobily/ts-belt

Documentation: https://mobily.github.io/ts-belt/

Introduction

After a few intensive months of after-hours work, I have finally published a new version of ts-belt (v3). As stated in the post title, TS Belt is a fast, modern, and practical utility library for functional programming in TypeScript.

Technical Background

TS Belt has been built in ReScript (and its Belt stdlib). ReScript generates highly performant JavaScript code, as well as it automatically generates TypeScript types with genType. Moreover, I've added a few codemods to the building process to provide even more code optimizations and cleaner TypeScript signatures.

Usage

If you're into FP and use TypeScript on a daily basis work, I assume you know at least one of these:

  • Ramda
  • Rambda
  • Remeda
  • lodash/fp

All of them follow FP principles: pipe operator, immutable data, no side-effects, etc. TS Belt does the same, however it provides the data-first approach for a single function call, which feels more natural, makes your code more readable and it’s much more developer-friendly, and the data-last approach for usage within the pipeline. Take a look at the following examples to see the difference:

// ⬇️ ts-belt → single function call
A.map([1, 2, 3, 4], value => value * 3)

// ⬇️ ramda/rambda → single function call
map(value => value * 3, [1, 2, 3, 4])

// ⬇️ ts-belt → pipe
pipe(
  [1, 2, 3, 4],
  A.filter(value => value % 2 === 0),
  A.map(value => value * 3),
)

// ⬇️ ramda/rambda → pipe
pipe(
  filter(value => value % 2 === 0),
  map(value => value * 3),
)([1, 2, 3, 4])

// ⬇️ lodash/fp → pipe
_.flow(
  _.filter(value => value % 2 === 0),
  _.map(value => value * 3),
)([1, 2, 3, 4])
Enter fullscreen mode Exit fullscreen mode

Last but not least, TS Belt provides two interesting implementations of data types:

  • Option - represents the existence and nonexistence of a value by wrapping it with the Option type
  • Result - describes the result of a certain operation without relying on exceptions

Performance

TS Belt is super fast, and I really mean it, it's even faster than the fastest library so far, Rambda.

Sample results (tested on MacBook Pro, M1 Pro, 2021):

map (single function call)

✔  @mobily/ts-belt  82,703,682.92  ops/sec  ±0.83%  (96 runs)  fastest
✔  remeda            2,966,512.35  ops/sec  ±1.53%  (92 runs)  -96.41%
✔  ramda            19,918,582.19  ops/sec  ±0.55%  (97 runs)  -75.92%
✔  rambda           81,584,073.11  ops/sec  ±0.88%  (87 runs)  -1.35%
✔  lodash/fp        13,133,226.26  ops/sec  ±0.59%  (99 runs)  -84.12%

filter (single function call)

✔  @mobily/ts-belt  48,676,101.83  ops/sec  ±0.29%  (100 runs)  fastest
✔  remeda            2,588,688.05  ops/sec  ±1.49%  (98 runs)   -94.68%
✔  ramda            16,662,990.83  ops/sec  ±0.78%  (97 runs)   -65.77%
✔  rambda           46,443,339.53  ops/sec  ±1.91%  (99 runs)   -4.59%
✔  lodash/fp         6,620,795.22  ops/sec  ±0.79%  (96 runs)   -86.40%

reduce (single function call)

✔  @mobily/ts-belt  44,890,901.88  ops/sec  ±0.17%  (102 runs)  fastest
✔  remeda            2,660,391.00  ops/sec  ±0.82%  (99 runs)   -94.07%
✔  ramda            10,199,240.77  ops/sec  ±0.65%  (97 runs)   -77.28%
✔  rambda           15,497,091.42  ops/sec  ±1.86%  (92 runs)   -65.48%
✔  lodash/fp         9,658,372.21  ops/sec  ±1.08%  (100 runs)  -78.48%

groupBy (single function call)

✔  @mobily/ts-belt  6,453,084.49  ops/sec  ±0.14%  (97 runs)  fastest
✔  remeda           1,783,616.20  ops/sec  ±1.05%  (95 runs)  -72.36%
✔  ramda            1,667,720.10  ops/sec  ±1.30%  (93 runs)  -74.16%
✔  rambda           6,100,470.04  ops/sec  ±1.29%  (94 runs)  -5.46%
✔  lodash/fp        3,123,622.49  ops/sec  ±0.89%  (97 runs)  -51.59%
Enter fullscreen mode Exit fullscreen mode
map → filter → reduce

✔  @mobily/ts-belt  254,251.22  ops/sec  ±0.20%  (99 runs)   fastest
✔  remeda            25,231.20  ops/sec  ±1.76%  (92 runs)   -90.08%
✔  ramda            131,950.08  ops/sec  ±0.41%  (98 runs)   -48.10%
✔  rambda           250,385.53  ops/sec  ±0.39%  (102 runs)  -1.52%
✔  lodash/fp         66,034.82  ops/sec  ±0.71%  (98 runs)   -74.03%

deepFlat → uniq → groupBy

✔  @mobily/ts-belt  2,297,096.07  ops/sec  ±0.20%  (99 runs)  fastest
✔  remeda             494,070.92  ops/sec  ±2.33%  (98 runs)  -78.49%
✔  ramda              281,192.43  ops/sec  ±0.97%  (93 runs)  -87.76%
✔  rambda           1,767,868.03  ops/sec  ±1.10%  (98 runs)  -23.04%
✔  lodash/fp          528,949.75  ops/sec  ±1.15%  (98 runs)  -76.97%
Enter fullscreen mode Exit fullscreen mode

Full benchmark results can be found here: https://mobily.github.io/ts-belt/benchmarks/introduction

Installation

To install ts-belt use either npm or yarn:

yarn add @mobily/ts-belt
Enter fullscreen mode Exit fullscreen mode
npm install @mobily/ts-belt --save
Enter fullscreen mode Exit fullscreen mode

Final Comments

TS Belt combines all of the good things you can find in other similar libraries: the developer-friendly data-first approach, good-looking and detailed documentation, great TypeScript support, and it's fast! 🚀

Let me know if you're willing to use ts-belt in your project! Also, don't forget to give a star to ts-belt on Github. Any feedback or suggestions are appreciated as well.

Happy coding! 😊

Discussion (0)