Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/joanllenas/ts.data.either
A Typescript implementation of the Either data type
https://github.com/joanllenas/ts.data.either
either left result right typescript
Last synced: 15 days ago
JSON representation
A Typescript implementation of the Either data type
- Host: GitHub
- URL: https://github.com/joanllenas/ts.data.either
- Owner: joanllenas
- Created: 2018-08-22T21:27:24.000Z (about 6 years ago)
- Default Branch: master
- Last Pushed: 2020-04-26T11:17:59.000Z (over 4 years ago)
- Last Synced: 2024-09-25T20:52:45.382Z (about 2 months ago)
- Topics: either, left, result, right, typescript
- Language: TypeScript
- Homepage:
- Size: 30.3 KB
- Stars: 15
- Watchers: 1
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Either
[![Build Status](https://travis-ci.org/joanllenas/ts.data.either.svg?branch=master)](https://travis-ci.org/joanllenas/ts.data.either)
[![npm version](https://badge.fury.io/js/ts.data.either.svg)](https://badge.fury.io/js/ts.data.either)The `Either` data type encapsulates the idea of a computation that may fail.
An `Either` value can either be `Right` some value or `Left` some error.
```ts
type Either = Right | Left;
```> If this is new to you, you may want to read the introductory article [Safer code with container types](https://blog.logrocket.com/safer-code-with-container-types-either-and-maybe/) about why and how to use this library.
## Install
```
npm install ts.data.either --save
```## Example
```ts
import { Either, tryCatch, andThen, withDefault } from './either';interface UserJson {
id: number;
nickname: string;
email: string;
}// throws if file does not exists
const readFile = (filename: string): string => {
const fileSystem: { [key: string]: string } = {
'something.json': `
[
{
"id": 1,
"nickname": "rick",
"email": "[email protected]"
},
{
"id": 2,
"nickname": "morty",
"email": "[email protected]"
}
]`
};
const fileContents = fileSystem[filename];
if (fileContents === undefined) {
throw new Error(`${filename} does not exists.`);
}
return fileContents;
};// Wraps the read file operation in an Either
const readFileContent = (filename: string): Either =>
tryCatch(
() => readFile(filename),
err => err
);// Wraps the json parsing operation in an Either
const parseJson = (json: string): Either =>
tryCatch(
() => JSON.parse(json),
err => new Error(`There was an error parsing this Json.`)
);// The pipeline function just makes function invocations flow
const pipeline = (initialValue: any, ...fns: Function[]) =>
fns.reduce((acc, fn) => fn(acc), initialValue);const usersFull: UserJson[] = pipeline(
'something.json',
(fname: string) => readFileContent(fname),
(json: Either) => andThen(parseJson, json),
(users: Either) => withDefault(users, [])
); // returns the Array of users because all intermediate operations have succeededconst usersEmpty: UserJson[] = pipeline(
'nothing.json',
(fname: string) => readFileContent(fname),
(json: Either) => andThen(parseJson, json),
(users: Either) => withDefault(users, [])
); // returns an empty Array because the readFile operations failed
```## Api
_(Inspired by elm-lang)_
### right
`right(value: T): Either`
Wraps a value in an instance of `Right`.
```ts
right(5); // Right(5)
```### left
`left(error: Error): Either`
Creates an instance of `Left`.
```ts
left(new Error('Something bad happened')); // Left(Error('Something bad happened'))
left(new Error('The calculation failed')); // Left(Error('The calculation failed'))
```### isRight
`isRight(value: Either): boolean`
Returns true if a value is an instance of `Right`.
```ts
isRight(left(new Error('Wrong!'))); // false
```### isLeft
`isLeft(value: Either): boolean`
Returns true if a value is not an instance of `Right`.
```ts
isLeft(right(5)); // false
isLeft(left('Hi!')); // true
isLeft(null); // true
```### withDefault
`withDefault(value: Either, defaultValue: T): T`
If `value` is an instance of `Right` it returns its wrapped value, if it's an instance of `Left` it returns the `defaultValue`.
```ts
withDefault(right(5), 0); // 5
withDefault(left(new Error('Wrong!')), 0); // 0
```### caseOf
`caseOf(caseof: {Right: (v: A) => B; Left: (err: Error) => B;}, value: Either): B`
Run different computations depending on whether an `Either` is `Right` or `Left` and returns the result.
```ts
caseOf(
{
Left: err => `Error: ${err.message}`,
Right: n => `Launch ${n} missiles`
},
right('5')
); // 'Launch 5 missiles'
```### map
`map(f: (a: A) => B, value: Either): Either`
Transforms an `Either` value with a given function.
```ts
const add1 = (n: number) => n + 1;
map(add1, right(4)); // Right(5)
map(add1, left(new Error('Something bad happened'))); // Left('Something bad happened')
```### tryCatch
`tryCatch(f: () => A, onError: (e: Error) => Error): Either`
Transforms a function (that might throw an exception) that produces `A` to a function that produces `Either`.
```ts
tryCatch(
() => JSON.parse(''),
err => err
); // Left('Unexpected end of JSON input')
```### andThen
`andThen(f: (a: A) => Either, value: Either): Either`
Chains together computations that may fail.
```ts
const removeFirstElement = (arr: T[]): T[] => {
if (arr.length === 0) {
throw new Error('Array is empty');
}
return arr.slice(1);
};const safeRemoveFirst = (arr: T[]): Either => {
try {
return right(removeFirstElement(arr));
} catch (error) {
return left(error);
}
};// The pipeline function just makes function invocations flow
const pipeline = (initialValue: any, ...fns: Function[]) =>
fns.reduce((acc, fn) => fn(acc), initialValue);const result: string[] = pipeline(
['a', 'b', 'c'],
safeRemoveFirst, // Right(['b', 'c'])
(arr: Either) => andThen(safeRemoveFirst, arr), // Right(['b'])
(arr: Either) => andThen(safeRemoveFirst, arr), // Right([])
(arr: Either) => andThen(safeRemoveFirst, arr), // Left(Error('Array is empty'))
(arr: Either) => andThen(safeRemoveFirst, arr), // Left(Error('Array is empty'))
(arr: Either) => withDefault(arr, [])
);console.log(result); // []
```