Skip to content

Commit

Permalink
Update
Browse files Browse the repository at this point in the history
  • Loading branch information
mobily committed Dec 1, 2021
1 parent 94ca40a commit 12d98a1
Show file tree
Hide file tree
Showing 129 changed files with 4,102 additions and 4,011 deletions.
2 changes: 1 addition & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@
"singleQuote": true,
"trailingComma": "all",
"arrowParens": "avoid",
"printWidth": 100
"printWidth": 80
}
26 changes: 7 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,27 +27,15 @@
<strong>Belt (<code>ReScript</code>) implementation in TypeScript.</strong>
</p>

## Motivation

I pretty much like ReScript, its features, tooling are great developer experience (as well as `Belt` stdlib). I wish it was my main tool on a daily basis work. Unfortunately it isn't, since most of the commercial projects I have been working on recently are built with TypeScript, which at this point is fully understandable from the business perspective.

## Features

- better type inference because of the `data-first` implementation
- built on top of ReScript (interesting combination, isn't it?), which automatically generates most of TS signatures and performant JS code

- benchmark results are various, but in general `ts-belt` always takes either the first or second place (remeda/rambda/ramda/lodash-fp), and especially comparing to `remeda`, which also follows the `data-first` approach, `ts-belt` is much faster, you can find the benchmarks results here
- `ts-belt` provides `Option` and `Result` implementation instead of `null` and `undefined` (get rid of them in your project!)
- high tests coverage
- lightweight, no external dependencies
- tree-shakeable
- fully documented

- [lightweight](https://bundlephobia.com/result?p=@mobily/ts-belt), no dependencies
- type safety, full TypeScript support
- high tests coverage
- all functions are [curried](https://medium.com/javascript-scene/curry-and-function-composition-2c208d774983)
- get rid of `undefined` and `null` values in your project in a convenient, functional way (use either `Option` or `Result`)
- 🚀 built with `ReScript`, which generates highly performant JavaScript code (see the benchmarks [here](benchmarks))
- 👀 provide more readable code, due to the `data-first` approach
- ✨ support for `TypeScript` and `Flow`
- 🛡 write more safer code with `Option` and `Result` types
- ✅ high tests coverage
- 🌲 tree-shakeable
- 📝 fully documented

## Installation

Expand Down
48 changes: 45 additions & 3 deletions __tests__/Array/drop.test.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,63 @@
import { A } from '../..'
import { expectType } from 'ts-expect'

import { A, pipe } from '../..'

describe('drop', () => {
it('provides correct types', () => {
expectType<ReadonlyArray<number>>(A.drop([1, 2, 3], 4))
expectType<ReadonlyArray<boolean>>(A.drop([true, true, false], 4))
})

it('returns the same values as the provided array', () => {
expect(A.drop([1, 2, 3], -1)).toEqual([1, 2, 3])
})

it('returns an empty array', () => {
expect(A.drop([], 0)).toEqual([])
expect(A.drop([1, 2, 3], 4)).toEqual([])
expect(A.drop([1, 2, 3], 3)).toEqual([])
expect(A.drop([1], 1)).toEqual([])
})

it('returns a new array that does not contain the first `n` items', () => {
expect(A.drop([], 0)).toEqual([])
expect(A.drop([1, 2, 3], 0)).toEqual([1, 2, 3])
expect(A.drop([1], 1)).toEqual([])
expect(A.drop([1, 2, 3], 1)).toEqual([2, 3])
expect(A.drop([[1], [2]], 1)).toEqual([[2]])
expect(A.drop([true, true, false], 2)).toEqual([false])
})

it('*', () => {
expect(A.drop([1], 1)).toEqual([])
expect(A.drop([1, 2, 3], 4)).toEqual([])
expect(A.drop([1, 2, 3], 2)).toEqual([3])
})
})

describe('drop (pipe)', () => {
it('provides correct types', () => {
expectType<ReadonlyArray<number>>(pipe([1, 2, 3], A.drop(4)))
expectType<ReadonlyArray<boolean>>(pipe([true, true, false], A.drop(4)))
})

it('returns the same values as the provided array', () => {
expect(pipe([1, 2, 3], A.drop(-1))).toEqual([1, 2, 3])
})

it('returns an empty array', () => {
expect(pipe([], A.drop(0))).toEqual([])
expect(pipe([1, 2, 3], A.drop(4))).toEqual([])
expect(pipe([1, 2, 3], A.drop(3))).toEqual([])
expect(pipe([1], A.drop(1))).toEqual([])
})

it('returns a new array that does not contain the first `n` items', () => {
expect(pipe([1, 2, 3], A.drop(0))).toEqual([1, 2, 3])
expect(pipe([1, 2, 3], A.drop(1))).toEqual([2, 3])
expect(pipe([[1], [2]], A.drop(1))).toEqual([[2]])
expect(pipe([true, true, false], A.drop(2))).toEqual([false])
})

it('*', () => {
expect(pipe([1, 2, 3, 4], A.drop(2))).toEqual([3, 4])
})
})
45 changes: 34 additions & 11 deletions __tests__/Array/filter.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,46 @@ import { expectType } from 'ts-expect'

import { A, pipe } from '../..'

const xs = [1, 2, 3, 4, 5, 6, 7, 8, 9]

// TODO: expectType
describe('filter', () => {
it('returns correct value', () => {
const result = A.filter(xs, value => value % 2 === 0)
expect(result).toEqual([2, 4, 6, 8])
it('provides correct types', () => {
A.filter([1, 2], value => {
expectType<number>(value)
return value % 2 === 0
})
A.filter([1, 'hello'], value => {
expectType<number | string>(value)
return !value
})
})

it('*', () => {
expect(A.filter([1, 2, 3, 4], value => value % 2 === 0)).toEqual([2, 4])
})
})

describe('filter (pipe)', () => {
it('returns correct value', () => {
const result = pipe(
xs,
A.filter(value => value % 2 === 0),
it('provides correct types', () => {
pipe(
[1, 2],
A.filter(value => {
expectType<number>(value)
return value % 2 === 0
}),
)
})

expect(result).toEqual([2, 4, 6, 8])
it('*', () => {
expect(
pipe(
[1, 2, 3, 4, 5, 6, 7, 8, 9],
A.filter(value => value % 2 === 0),
),
).toEqual([2, 4, 6, 8])
expect(
pipe(
['hello', 'wo', 'rld'],
A.filter(value => value.length < 4),
),
).toEqual(['wo', 'rld'])
})
})
14 changes: 7 additions & 7 deletions __tests__/Array/get.test.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { A } from '../..'
import { A, O } from '../..'

describe('get', () => {
it('returns None', () => {
expect(A.get([], 0)).toBeNone()
expect(A.get([1, 2, 3], 3)).toBeNone()
expect(A.get([], 0)).toEqual(O.None)
expect(A.get([1, 2, 3], 3)).toEqual(O.None)
})

it('returns Some', () => {
expect(A.get([1, 2, 3], 0)).toBeSome(1)
expect(A.get([0, 2, 3], 0)).toBeSome(0)
expect(A.get([true, true, false], 2)).toBeSome(false)
expect(A.get([[1], [2]], 1)).toBeSome([2])
expect(A.get([1, 2, 3], 0)).toEqual(O.Some(1))
expect(A.get([0, 2, 3], 0)).toEqual(O.Some(0))
expect(A.get([true, true, false], 2)).toEqual(O.Some(false))
expect(A.get([[1], [2]], 1)).toEqual(O.Some([2]))
})
})
14 changes: 7 additions & 7 deletions __tests__/Array/getBy.test.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
import { A } from '../..'
import { A, O } from '../..'

describe('getBy', () => {
it('returns None', () => {
expect(A.getBy([1, 2, 3], n => n === 0)).toBeNone()
expect(A.getBy([false, false, false], state => state)).toBeNone()
expect(A.getBy([1, 2, 3], n => n === 0)).toEqual(O.None)
expect(A.getBy([false, false, false], state => state)).toEqual(O.None)
expect(
A.getBy([{ prop: null }, { prop: false }, { prop: undefined }], obj => Boolean(obj.prop)),
).toBeNone()
).toEqual(O.None)
})

it('returns Some', () => {
expect(A.getBy(['a', 'ab', 'bc'], str => str.length === 2)).toBeSome('ab')
expect(A.getBy([1, 2, 3], value => value === 2)).toBeSome(2)
expect(A.getBy(['a', 'ab', 'bc'], str => str.length === 2)).toEqual(O.Some('ab'))
expect(A.getBy([1, 2, 3], value => value === 2)).toEqual(O.Some(2))
expect(
A.getBy([{ prop: 'ab' }, { prop: 'abc' }, { prop: 'bcd' }], obj => obj.prop.length > 2),
).toBeSome({ prop: 'abc' })
).toEqual(O.Some({ prop: 'abc' }))
})
})
14 changes: 7 additions & 7 deletions __tests__/Array/head.test.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { A } from '../..'
import { A, O } from '../..'

describe('head', () => {
it('returns None', () => {
expect(A.head([])).toBeNone()
expect(A.head([])).toEqual(O.None)
})

it('returns Some', () => {
expect(A.head([1, 2, 3])).toBeSome(1)
expect(A.head([0, 2, 3])).toBeSome(0)
expect(A.head([false, true, true])).toBeSome(false)
expect(A.head([{ prop: 1 }, { prop: 2 }])).toBeSome({ prop: 1 })
expect(A.head([[1], [2]])).toBeSome([1])
expect(A.head([1, 2, 3])).toEqual(O.Some(1))
expect(A.head([0, 2, 3])).toEqual(O.Some(0))
expect(A.head([false, true, true])).toEqual(O.Some(false))
expect(A.head([{ prop: 1 }, { prop: 2 }])).toEqual(O.Some({ prop: 1 }))
expect(A.head([[1], [2]])).toEqual(O.Some([1]))
})
})
30 changes: 25 additions & 5 deletions __tests__/Option/filter.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { pipe, O } from '../..'
import { pipe, O, A } from '../..'

describe('filter', () => {
it('returns None', () => {
Expand All @@ -7,13 +7,13 @@ describe('filter', () => {
O.fromNullable(null),
O.filter(_ => false),
),
).toBeNone()
).toEqual(O.None)
expect(
pipe(
O.fromNullable(null),
O.filter(_ => true),
),
).toBeNone()
).toEqual(O.None)
expect(
pipe(
O.fromNullable([3, 5, 7]),
Expand All @@ -22,7 +22,7 @@ describe('filter', () => {
return fst === 1
}),
),
).toBeNone()
).toEqual(O.None)
})

it('returns Some', () => {
Expand All @@ -34,6 +34,26 @@ describe('filter', () => {
return fst === 1
}),
),
).toBeSome([1, 2, 3])
).toEqual(O.Some([1, 2, 3]))
})

it('*', () => {
const { Some, None } = O

expect(
pipe(
O.fromNullable([3, 6, 9]),
O.flatMap(A.get(0)),
O.filter(value => value === 3),
),
).toEqual(Some(3))

expect(
pipe(
O.fromNullable([3, 6, 9]),
O.flatMap(A.get(0)),
O.filter(value => value === 0),
),
).toEqual(None)
})
})
20 changes: 16 additions & 4 deletions __tests__/Option/flatMap.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,22 @@ describe('flatMap', () => {
O.fromNullable(null),
O.flatMap(_ => O.Some(1)),
),
).toBeNone()
).toEqual(O.None)

expect(
pipe(
O.fromNullable(undefined),
O.flatMap(_ => O.None),
O.flatMap(_ => O.Some('value')),
),
).toBeNone()
).toEqual(O.None)

expect(
pipe(
O.fromNullable('value'),
O.flatMap(_ => O.None),
),
).toBeNone()
).toEqual(O.None)
})

it('returns Some', () => {
Expand All @@ -31,6 +31,18 @@ describe('flatMap', () => {
O.fromNullable('value'),
O.flatMap(_ => O.Some('this is fine')),
),
).toBeSome('this is fine')
).toEqual(O.Some('this is fine'))
})

it('*', () => {
const { Some } = O
expect(
pipe(
O.fromNullable('hello'),
O.flatMap(value => {
return value.endsWith('lo') ? O.Some(`${value} world!`) : O.None
}),
),
).toEqual(Some('hello world!'))
})
})
28 changes: 19 additions & 9 deletions __tests__/Option/fromFalsy.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,27 @@ import { O } from '../..'

describe('fromFalsy', () => {
it('returns None', () => {
expect(O.fromFalsy(null)).toBeNone()
expect(O.fromFalsy(undefined)).toBeNone()
expect(O.fromFalsy(0)).toBeNone()
expect(O.fromFalsy('')).toBeNone()
expect(O.fromFalsy(false)).toBeNone()
expect(O.fromFalsy(null)).toEqual(O.None)
expect(O.fromFalsy(undefined)).toEqual(O.None)
expect(O.fromFalsy(0)).toEqual(O.None)
expect(O.fromFalsy('')).toEqual(O.None)
expect(O.fromFalsy(false)).toEqual(O.None)
})

it('returns Some', () => {
expect(O.fromFalsy('value')).toBeSome('value')
expect(O.fromFalsy(1)).toBeSome(1)
expect(O.fromFalsy([])).toBeSome([])
expect(O.fromFalsy({})).toBeSome({})
expect(O.fromFalsy('value')).toEqual(O.Some('value'))
expect(O.fromFalsy(1)).toEqual(O.Some(1))
expect(O.fromFalsy([])).toEqual(O.Some([]))
expect(O.fromFalsy({})).toEqual(O.Some({}))
})

it('*', () => {
const { Some, None } = O

expect(O.fromFalsy(1)).toEqual(Some(1))
expect(O.fromFalsy('hello world')).toEqual(Some('hello world'))
expect(O.fromFalsy([])).toEqual(Some([]))
expect(O.fromFalsy(0)).toEqual(None)
expect(O.fromFalsy('')).toEqual(None)
})
})
Loading

0 comments on commit 12d98a1

Please sign in to comment.