hkt-toolbelt

Functional and composable type utilities

README

hkt-toolbelt


Functional and composable type utilities

hkt-toolbeltis a collection of type-level utilities that can be mapped and combined in functional ways using higher-kinded types.

Our composable and compile time-efficient types enable users to write expressive and readable type-level code without getting mired in complexity.

We support all of the type categories that you'll need (List, Object, String, Union, Function, Boolean, Number, and more), plus our very own Kindand Combinatorutilities for composing types.

We're always adding new and exciting features, so stay tuned!

What is a hkt?


hktstands for "higher-kinded type"


TypeScript has two distinct constructions: types, and generics.

Type: A compile-time expression used to describe a value.
Generic: A type "template" that resolves into a new type when instantiated with type arguments.

Generics are notfirst-class citizens in TypeScript: a) they can't be referenced without being supplied all of their type arguments, b) they can't be passed in as arguments to other generics, and c) they can't be returned by other generics. These were fundamental limitations of the TypeScript language ... until now.

hkt-toolbeltintroduces two new constructions:

Kind: A compile-time expression that is used to describe a type, and is parameterized so that it can be applied to an argument type.
Generic Kind: A generic type that returns a kind (a.k.a. "higher-kinded type").

For convenience, we will use "kind"to refer to "higher-kinded types"as well.


Using kinds allows us to represent new types that cannot be expressed using generics alone. Even for types that arerepresentable using generics, we can use kinds to provide a more ergonomic API and elegant implementation.

Getting Started


Install


  1. ``` shell
  2. > npm install hkt-toolbelt
  3. > yarn add hkt-toolbelt
  4. ```

Import


You can selectively import the kind categories that you need.

  1. ``` ts
  2. import {
  3.   $, $$, $N,
  4.   Boolean, Conditional, Function,
  5.   List, Object, String, Union,
  6.   Number, NaturalNumber,
  7.   Kind, Type, Combinator, Parser,
  8. } from "hkt-toolbelt";
  9. ```

You also have the option to load individual type utilities from subpaths.

  1. ``` ts
  2. import { $, $$, $N } from "hkt-toolbelt";
  3. import { Map, Filter, Reduce } from "hkt-toolbelt/list";
  4. import { Equals, Extends, If } from "hkt-toolbelt/conditional";
  5. ```

Usage


1) $: Apply a Function to an Argument


-lt;KIND, ARG>


The kind utilities in hkt-toolbeltare curried, unary type-level functions that can be applied to a single type argument using the $applicator.

  1. ``` ts
  2. import { $, Function, List } from "hkt-toolbelt";

  3. type HelloWorld = $<$<List.Push, "world">, ["hello"]>;  // ["hello", "world"]

  4. type OneTwoThree = $<
  5.   $<List.Filter, $<Conditional.Extends, number>>,
  6.   [1, "foo", 2, 3, "bar"]
  7. >; // [1, 2, 3]
  8. ```

  1. ``` js
  2. // In javascript, this would be..
  3. const helloWorld = ((arr) => [...arr, "world"])(["hello"]);

  4. const oneTwoThree = ((arr) => arr.filter((e) => typeof e === "number"))([1, "foo", 2, 3, "bar"]);
  5. ```

2) $N: Pass Multiple Arguments into an uncurried Function


$N<KIND, [ARG1, ARG2, ...]>


What if your function needs multiple arguments? Simply use the $Noperator. Now you can supply arguments in order using a tuple, instead of heavily nested $calls.

  1. ``` ts
  2. import { $, $N, List, NaturalNumber } from "hkt-toolbelt";

  3. // Example 1: full application (invoked with all three arguments)
  4. type ReduceSum1to5 = $N<List.Reduce, [
  5.   NaturalNumber.Add,  // callback
  6.   0,                  // initial value
  7.   [1, 2, 3, 4, 5]     // target array
  8. ]>;  // 15

  9. // Example 2: partial application (invoked with only the first two arguments)
  10. type ReduceSum = $N<List.Reduce, [NaturalNumber.Add, 0]>;

  11. type ReduceSum1to5 = $<ReduceSum, [1, 2, 3, 4, 5]>;  // 15
  12. type ReduceSum1to10 = $<ReduceSum, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]>;  // 55
  13. ```

  1. ``` js
  2. // In javascript, this would be..

  3. // Example 1
  4. const reduceSum1to5$N = [1, 2, 3, 4, 5].reduce((acc, curr) => acc + curr, 0);

  5. // Example 2
  6. const reduceSum = (arr) => arr.reduce((acc, curr) => acc + curr, 0);

  7. const reduceSum1to5 = reduceSum([1, 2, 3, 4, 5]);
  8. const reduceSum1to10 = reduceSum([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
  9. ```

3) $$: Pipe an Argument through Multiple Functions


$<[KIND1, KIND2, ...], ARG>


What if you want to compose multiple functions over a single argument? This time, use the $$operator to supply the functions in order using a tuple.

  1. ``` ts
  2. import { $, $$, List, String } from "hkt-toolbelt";

  3. type UnshiftPushJoin = $$<[
  4.     $<List.Unshift, "first">,  // ["first", "second"]
  5.     $<List.Push, "third">,     // ["first", "second", "third"]
  6.     $<String.Join, ", ">,      // "first, second, third"
  7.   ], ["second"]>;
  8. ```

  1. ``` js
  2. // In javascript, this would be..
  3. const unshiftPushJoin =
  4.   ((arr) => arr.join(", "))(
  5.     (((arr) => [...arr, "third"])(
  6.       ((arr) => ["first", ...arr])(["second"])
  7.   )));
  8. ```

Guides


We have additional resources to help you get started with hkt-toolbelt, that go in depth on the concepts and usage.

[Custom Kinds] * - How do I create my own higher-kinded types?
[Kind Constraints] * - How do I constrain a higher-kinded type's input?
[HK-Type Encoding] * - Details on the internal encoding.

Similar Projects


Inspired by ts-toolbelt
Awesome TS learning resource: type-challenges
Value-level utilities: lodash

Table of Contents


Note:Many examples here are out of date, as many higher-kinded types have been modified to be more partially applicable.


Generally, types such as `List.Filter`are now `$`.

More updates on API documentation coming soon.

The curried nature of the functions in this library is intended to be utilized to compose types using point-free style.

As a general principle, API types are written to first take in operations, and then the data to be operated on.

Boolean Types


### `Boolean.And`

The Andtype takes in a boolean and returns a function that takes in another boolean and returns the result of the two booleans being &&'d together.

  1. ``` ts
  2. import { $, Boolean } from "hkt-toolbelt";

  3. type Result = $<Boolean.And<true>, false>; // false
  4. ```

### `Boolean.Or`

The Ortype takes in a boolean and returns a function that takes in another boolean and returns the result of the two booleans being ||'d together.

  1. ``` ts
  2. import { $, Boolean } from "hkt-toolbelt";

  3. type Result = $<Boolean.Or<true>, false>; // true
  4. ```

Boolean.Not


The Nottype takes in a boolean and returns the opposite boolean.

  1. ``` ts
  2. import { $, Boolean } from "hkt-toolbelt";

  3. type Result = $<Boolean.Not, true>; // false
  4. ```

Combinator Types


Combinator.Self


The Selfkind returns itself. This means it can be applied with $ infinitely.

  1. ``` ts
  2. import { $, Combinator } from "hkt-toolbelt";

  3. type Result = $<$<Combinator.Self, "foo">, "foo">; // Combinator.Self
  4. ```

Combinator.ApplySelf


The ApplySelfkind takes in a kind, and applies that kind to itself. This can be used to model recursion for higher-order types.

  1. ``` ts
  2. import { $, Combinator } from "hkt-toolbelt";

  3. type Result = $<Combinator.ApplySelf, Function.Identity>; // Function.Identity
  4. ```

Conditional Types


### `Conditional.Equals`

The Equalstype is used to check if a type is equal to another type. It is equivalent to the A extends B ? (B extends A ? true : false) : falsesyntax in TypeScript.

Equalsreturns a higher-kinded-type function that takes a type and returns a boolean.

  1. ``` ts
  2. import { $, Conditional } from "hkt-toolbelt";

  3. type Result = $<$<Conditional.Equals, "foo">, "bar">; // false
  4. ```

### `Conditional.Extends
`

The Extendstype is used to check if a type is a subtype of another type. It is equivalent to the A extends B ? true : falsesyntax in TypeScript.

The first type passed in is the supertype, and the second type passed in is the subtype.

Extendsreturns a higher-kinded-type function that takes a type and returns a boolean.

  1. ``` ts
  2. import { $, Conditional } from "hkt-toolbelt";

  3. type Result = $<$<Conditional.Extends, string>, "bar">; // true
  4. ```

### `If`

The `If`type is used to conditionally return a type. It is equivalent to the `P extends true ? T : E`syntax in TypeScript, but can be supplied with its argument `X`in a point-free style.

Iftakes in a predicate, a true type, and a false type. It returns a higher-kinded-type function that takes in a type and returns the result of the associated branch.

  1. ``` ts
  2. import { $, Conditional } from "hkt-toolbelt";

  3. type Result = $<
  4.   Conditional.If<
  5.     Conditional.Equals<"foo">,
  6.     String.Append<"bar">,
  7.     String.Append<"baz">
  8.   >,
  9.   "foo"
  10. >; // "foobar"
  11. ```

This is a higher-kinded type used to designate type-level control flow.

Function Types


Function


The Functiontype is a supertype of all functions, i.e. all functions are a subtype of Function. It is not a kind and cannot be applied.

### `Function.Constant
`

The Constanttype takes in a type and returns a function that takes in any type and returns the original type. It ignores its applied input and always returns the configured type.

  1. ``` ts
  2. import { $, Function } from "hkt-toolbelt";

  3. type Result = $<$<Function.Constant, "foo">, number>; // "foo"
  4. ```

Function.Identity


The Identitytype takes in a type and returns the same type, on the higher-kinded-type level.

  1. ``` ts
  2. import { $, Function } from "hkt-toolbelt";

  3. type Result = $<Function.Identity, "foo">; // "foo"
  4. ```

Kind Types


### `Kind`

The Kindtype denotes a type function that may be applied to a type using $.

The Kind type can optionally be provided a function type to increase the specificity of its internal parameter and return types. This is used to create new kinds.

### `Kind.Composable`

The Composabletype checks whether a tuple of kinds are composable. A tuple of kinds is composable if the output of kind Nis a subtype of the input of kind N−1.

  1. ``` ts
  2. import { $, Kind, String } from "hkt-toolbelt";

  3. type Result = $<Kind.Composable, [String.Append<"bar">, String.Append<"foo">]>; // true
  4. ```

### `Kind.Compose`

The Composetype takes in a tuple of type functions, and composes them into one type function.

Composechecks that the tuple of kinds is composable, and returns a higher-kinded-type function that takes in a type and returns the result of the composition.

Composeexecutes functions from right to left, i.e. the last function in the tuple is executed first - as is traditional in mathematics.

  1. ``` ts
  2. import { $, Kind, String } from "hkt-toolbelt";

  3. type Result = $<Kind.Compose<[String.Append<"bar">, String.Append<"foo">]>, "">; // "foobar"
  4. ```

### `Kind.Pipe`

The Pipetype takes in a tuple of type functions, and pipes them into one type function. This operates from left to right, i.e. the first function in the tuple is executed first. This is the opposite order of Compose.

Pipeis often more intuitive for programmers since it reads in order of execution. This is what $$uses internally.

  1. ``` ts
  2. import { $, Kind, String } from "hkt-toolbelt";

  3. type Result = $<Kind.Pipe<[String.Append<"foo">, String.Append<"bar">]>, "">; // "foobar"
  4. ```

Kind._


The _type represents the 'unique placeholder type' used in type functions before application. Kind._is used by $for application.

List Types


### `List.Map`

The Mapfunction takes in a type function, and returns a higher kinded type that takes in a tuple type. It applies the given type function over every element in the tuple.

  1. ``` ts
  2. import { $, List, String } from "hkt-toolbelt";

  3. type Result = $<List.Map<String.Append<"bar">>, ["foo", "baz"]>; // ["foobar", "bazbar"]
  4. ```

### `List.Find`

The Findfunction takes in a type function, then a tuple, and returns the first tuple element for which the finder function returns true. If no such element exists, Findreturns never.

  1. ``` ts
  2. import { $, List, String } from "hkt-toolbelt";

  3. type Result = $<List.Find<String.StartsWith<"foo">>, ["bar", "foobar"]>; // "foobar"
  4. ```

### `List.Filter`

The Filterfunction takes in a type function, and a tuple, and returns a tuple in-order of the input tuple, whereby only elements for which the filter function returns trueremain in the resultant tuple.

  1. ``` ts
  2. import { $, List, String } from "hkt-toolbelt";

  3. type Result = $<List.Filter<String.StartsWith<"foo">>, ["bar", "foobar"]>; // ["foobar"]
  4. ```

### `List.Append`

The Appendfunction takes in a type, and a tuple, and applies the type such that it is appended to the end of the provided tuple.

  1. ``` ts
  2. import { $, List } from "hkt-toolbelt";

  3. type Result = $<List.Append<"bar">, ["foo", "baz"]>; // ["foo", "baz", "bar"]
  4. ```