boost-ts
TypeScript Library to boost functional programming
boost-ts
TypeScript Library to boost functional programming
This library includes useful generic types that operate TypeScript types, e.g. push/pop/zip for type tuples, and useful general type-safe functions that returns complicated types.
-
Function Library
- partial : type-safe partial parameter binding similar to C++ boost library
- mkobjmap : type-safe map function for key-value object
- mergeobj : type-safe recursive merge function for key-value object
- bundle : type-safe partial parameter binding for multiple functions
-
Type Library
- type tuple
- type map
Function Library
partial
This library offers a partial function call with flexible argument binding. Of course, it's type safe.
import { partial, _1, _2 } from "boost-ts"
function sub (a:number, b:number):number {
return a - b
}
// bind 2nd argument
const sub10 = partial(sub, _1, 10) // type :: (a:number)=>number
console.log(sub10(100)) // output is 90
// swap 1st and 2nd argument
const reverse_sub = partial(sub, _2, _1) // type :: (a:number, b:number)=>number
console.log(reverse_sub(10, 100)) // output is 90
mkobjmap
Type-safe map for object.
By using Object.entries()
and reduce()
, we can implement a map
-like fnction for Typescript objects.
////////////////////////////////////////////////////////////////
/// Unexpected Case
////////////////////////////////////////////////////////////////
type Box<T> = { value: T }
function boxify<T>(t: T):Box<T> {
return { value: t }
}
const data = {
name: "John",
age: 26
}
const unexpected = Object.entries(data).reduce((acc, [key, value])=>{
return {
...acc,
[key]: boxify(value)
}
}, {})
// unexpected.name is ERROR!!
//
// Even with more typing, type will be like ...
// {
// name: Box<number> | Box<string>
// age: Box<number> | Box<string>
// }
We want the type { name: Box<string>, age: Box<number> }
in this case.
import { mkmapobj } from "boost-ts"
////////////////////////////////////////////////////////////////
// Expected Case
////////////////////////////////////////////////////////////////
type BoxMapType<T> = { [P in keyof T]: [T[P], Box<T[P]>] }
// To reuse 'mapobj', we can list all possible types as tuple
type BoxKeyType = [string, number, boolean, string[], number[]]
// Make 'map' type with Mapped Tuple Type, and apply
const mapobj = mkmapobj<BoxMapType<BoxKeyType>>()
// The dataBox type is `{ name: Box<string>, age: Box<number> }`
const dataBox = mapobj(data, boxify)
chai.assert.equal(dataBox.name.value, data.name)
chai.assert.equal(dataBox.age.value, data.age)
bundle
Supposed we have an interface for set of file operations,
// What we have
interface FileOper {
dirname: (config:Config) => string,
read: (config:Config, name:string) => string
write: (config:Config, name:string, content:string) => number
}
and Config
is a singleton, then we expect such interface with curried functions.
// What we expect
interface CurriedFileOper {
dirname: () => string,
read: (name:string) => string
write: (name:string, content:string) => number
}
In such cases, bundle
is convenient.
import { bundle } from "boost-ts"
// 'bundle' curries bunch of functions
const curriedFileOper:CurriedFileOper = bundle(config, fileOper)
mergeobj
Type-safe merge of key-value objects
const recordA = {
personal: {
name: "John",
age: "26"
}
}
const recordB = {
personal: {
age: 26,
nationality: "American"
}
}
const merged = mergeobj(recordA, recordB)
/*
The type of 'merged' is
{
personal: {
name: string,
age: number,
nationality: string
}
}
*/
Type Library
This library for Typescript types offers tuple type operation, like Push, Pop, Find, Select, Zip etc.
I hope we can avoid to add "as any" for the complicated type of Typescript functions with this library. As design policy, recursive type definition is avoided as much as possible because it sometimes causes a compile error when initiating types.
Add
import { Push, Pop, Head, Tail } from "boost-ts"
Push
Add a type to the head of type tuple.
// Target = [boolean, string, number]
type Target = Push<boolean, [string, number]>
Pop
Remove a type from the head of type tuple.
// Target = [string, number]
type Target = Pop<[boolean, string, number]>
Head
Get the head of type tuple.
// Target = boolean
type Target = Head<[boolean, string, number]>
Reverse
Reverse the order of type tuple.
// Target = [number, string, boolean]
type Target = Reverse<[boolean, string, number]>
Filter
Filter a type from type tuple (incl. recursive call)
// Target = [boolean, number]
type Target = Filter<string, [boolean, string, number]>
Select
Select a type from type tuple (incl. recursive call)
// Target = [string, number]
type Target = Select<string|number, [boolean, string, number]>
Zip
Zip two type tuples.
// Target = [ [1, boolean], [2, string, [3, number] ]
type Target = Zip<[1, 2, 3], [boolean, string, number]>
SelectObject
Select properties from object type.
type Source = {
str1: string,
num1: number,
bool1: boolean,
str2: string,
num2: number
bool2: boolean
}
// Target = {
// str1: string,
// str2: string
// }
type Target = SelectObject<Source, string>
FilterObject
Filter properties from object type.
type Source = {
str1: string,
num1: number,
bool1: boolean,
str2: string,
num2: number
bool2: boolean
}
// Target = {
// num1: number,
// num2: number,
// bool1: boolean,
// bool2: boolean
// }
type Target = FilterObject<Source, string>
Decrease
Decrease a number type.
// Target = 3
type Target = Decrease<4>
Comp
Compare two number types.
// Target1 = -1
type Target1 = Comp<1, 2>
// Target2 = 0
type Target2 = Comp<2, 2>
// Target3 = 1
type Target3 = Comp<2, 1>
- Some code of this library is based on this stackoverflow article.
- The API of partial function is inspired by Boost C++ library
- Thanks to the blog Suppress Error of type level programming of TypeScript. This library will be nothing without workarounds suggested by this blog. Japanese version is also published.
- typepark is an excellent library that provides the large collection of typescript types manipulation.