@siegrift/tsfunct

Tsfunct is a **T**ype**S**cript **funct**ional library made directly for TS with its static typesystem in mind.

Usage no npm install needed!

<script type="module">
  import siegriftTsfunct from 'https://cdn.skypack.dev/@siegrift/tsfunct';
</script>

README

Tsfunct CircleCI

Tsfunct is a TypeScript functional library made directly with and for TS with its static typesystem in mind.

Installation

To install this package run either:

yarn add @siegrift/tsfunct

or if you use npm

npm i @siegrift/tsfunct --save

Important: Some functions (set and friends) work reliably only with TS ^3.7, because of this issue. Also, this library will be using latest TS features when needed. Keep this in mind if you are trying to use it in your project.

API and documentation

Documentation is automatically generated from source code and can be found at github pages here.

You can also play with the library on CodeSandbox.

You can read the list and sources of all helpers in the src/lib folder here.

Motivation

There are two big libraries which provide helper functions for JS/TS. These are lodash and ramda. Both of these libraries are made for JS and the TS typings for many functions are poor. Also, these functions aim to be as general as possible, which makes it harder or even impossible to type properly.

There are certain helpers (mainly for immutable object manipulation) which can be typed better. Let's take a look at get(obj, path) helper in both lodash (4.14.132) and ramda (0.26.9), when using it on a strongly typed TS object.

Weak typed result
(Lodash gets it at least correct, but cannot determine the result type. Ramda allows you to pass a type that is being returned, but you can omit it and produce incorrect result type)

No compile error
(There are no TS warnings about accessing value on nonexistent path)

Lets look what you can get by using get(obj, path) with TSfunct.

Strongly typed get helper
There are many advantages of this helper:

  • The result has correct type
  • The path can be autocompleted and must be able to exist in the object
  • Handles arrays, optional and nullable values (even in intermediate objects)

Update helper
When you call update for the first time, value in update function can be undefined (if any intermediate value doesn't exist). However, when calling it for a second time, it is guaranteed that the values on the path exist.

Refer to documentation, source code and tests for more examples.

Immutability

All functions in this library are effectively immutable. That means that if you use the helpers according to their idiomatic usage, library is immutable. However, there are no deep copies created for the source values and you are able to modify the original entity if you try really hard.

const original = [{ a: 0 }, { a: 1 }, { a: 2 }]
const mapped = map(original, (val) => (val.a = 3))
// 'mapped'  will equal to [3, 3, 3]
// 'original' will equal to [{ a: 3 }, { a: 3 }, { a: 3 }]

Chaining

TLDR: It is a bad idea. If you want to learn more, read this article

Functional programming style

Most of the functions in this library are written imperatively (e.g. const get = (object, path) => implementation compared to traditional functional const get = (path) => (object) => implementation) for better typing and autocompletion support. These helpers aren't composable together and if you would like to do multiple transformations you would have to either nest the calls (which hurts readability) or introduce unnecessary local variables.

For this reason, there are also functional alternatives of most common methods (in the future maybe all of them), which offer the same type guarantees and their imperative clones. These fp helpers have prefix fp (e.g. functional version of set helper is called fpSet).

(If you are looking for more FP helpers have a look at monocle or fp ts or lodash fp)

Codebase overview

Each helper is written in its own module without depending on other helper. This allows you to copy the source of single helper you want without installing the whole library.

Limitations

Most of the helpers are typed manually and have some restrictions on its arguments. For example, path array can be up to X elements only in some helpers...

Bear in mind that TS is unsound! Types might easily lie to you if you are not careful. For example,

const arr: number[] = [1, 2, 3]
const num: number = get(arr, [999]) // this line won't trigger TS error!
console.log(num) // undefined!

Other limitation is for example TS path autocompletion for immutability helpers, which I reported and is tracked in this issue and will be fixed in the future.

Issues

Each helper is heavily tested and I try to make the library as stable as possible. In case there is a bug or unwanted behavior, please create an issue.

Contribution

If you would like to fix an issue or create another helper, feel free to create a PR for it. To contribute just follow these steps:

  1. fork the repository
  2. make sure you have yarn installed and run: yarn
  3. create a new branch with you feature
  4. commit & push
  5. create PR to the original repo