# Option

A library with a small set of functions that should give you the ability to write a program without `undefined` or `null`, which makes your program easier to reason about and safer.

## Installation

This package can be used with both **Node** and **Deno**.

### Node

```bash
npm i @wentools/option
```

### Deno

#### From a dependency file

```typescript
// Preferably in deps.ts
export {
  some,
  none,
} from 'https://gitlab.com/wentools/option/-/raw/v0.8.6/src/mod.ts'
```

#### Consume directly

```typescript
import {
  some,
  none,
} from 'https://gitlab.com/wentools/option/-/raw/v0.8.6/src/mod.ts'
```

## Basic usage

The functions `some` and `none` creates the `Option`. If it was the `some` function that created the `Option` it will hold a value. If it was the `none` function it will not hold a value.

Using the methods on the `Option` you can safely apply transformations on the value, or resolve the value in a safe way.

```typescript
import { some, none } from '@wentools/option'

const name1 = {
  first: 'John',
  middle: some('William'),
  last: 'Doe',
}

const name2 = {
  first: 'John',
  middle: none(),
  last: 'Doe',
}

console.log(
  `Middle name of ${name1.first} is ${name1.middle
    .map((n) => n.toUpperCase())
    .unwrapOr('nothing')}`
)
// Middle name of John is WILLIAM

console.log(
  `Middle name of ${name2.first} is ${name2.middle
    .map((n) => n.toUpperCase())
    .unwrapOr('nothing')}`
)
// Middle name of John is nothing
```

```typescript
import { assumeOption } from '@wentools/option'

const unsafeFunction = (): string | undefined => {
  /* implementation left out for brevity */
}

assumeOption(unsafeFunction()).match({
  some: (x) => console.log(x),
  none: () => console.log('No data'),
})
```

## Functions for creating `Option`

### some

`some(DataType): Option<DataType>`

Use this function to create an `Option` that holds a value.
If using TypeScript it will try to infer the type. You can also provide the type if you want to be explicit.

```typescript
const fruit = some('Apple')
fruit.unwrapOr('Banana')
// returns 'Apple'
```

```typescript
// Explicit type
const fruit = some<string>('Apple')
fruit.unwrapOr('Banana')
// returns 'Apple'
```

### none

`none(): Option<DataType>`

Use this function to create an `Option` that holds no value.
If using TypeScript it will try to infer the type.
If it can't infer the type it is encouraged to provide the type.

```typescript
const fruit = none()
fruit.unwrapOr('Banana')
// returns 'Banana'
```

```typescript
// Explicit type
const fruit = none<string>()
fruit.unwrapOr('Banana')
// returns 'Banana'
```

### assumeOption

`(DataType | undefined | null): Option<DataType>`

Use this function to create an `Option` that might hold a value.
A good use case is if you're using a library where you have no control but want to use `Option` to safely consume it's data.

```typescript
const fruits = ['Apple']
const fruit = assumeOption(fruits[0])
fruit.unwrapOr('Banana')
// returns 'Apple'
```

```typescript
const fruits = ['Apple']
const fruit = assumeOption(fruits[99])
fruit.unwrapOr('Banana')
// returns 'Banana'
```

## Utility functions

### transposeOptionToPromise

`transposeOptionToPromise(Option<Promise<DataType>>): Promise<Option<DataType>>`

Transposes an `Option` holding a `Promise` to a `Promise` holding and `Option`

```typescript
const fruit = some(await 'Apple')
await fruit.transposeOptionToPromise().unwrap()
// returns 'Apple'
```

## Option methods

### unwrap

`unwrap(): DataType`

Returns value if `Option` is `some`. Will throw if `Option` is `none`.

```typescript
const fruit = some('Apple')
fruit.unwrap()
// returns 'Apple'
```

```typescript
const fruit = none()
fruit.unwrap()
// Will throw error.
// It not recommended to use `unwrap`
// unless you know it's a 'some'.
```

### unwrapOr

`unwrapOr(DataType): DataType`

Returns value of `Option` if it is `some`. If `Option` is `none` it will return the value of the `or` parameter.

```typescript
const fruit = some('Apple')
fruit.unwrapOr('Banana')
// returns 'Apple'
```

```typescript
const fruit = none()
fruit.unwrap('Banana')
// returns 'Banana'
```

### unwrapOrElse

`unwrapOrElse(() => DataType): DataType`

Returns value of `Option` if it is `some`. If `Option` is `none` it will run the `orElse` function.

```typescript
const fruit = some('Apple')
fruit.unwrapOrElse((x) => x.toUpperCase())
// returns 'Apple'
```

```typescript
const fruit = none()
fruit.unwrapOrElse((x) => x.toUpperCase())
// returns 'BANANA'
```

### match

`match({ some(DataType): MatchExpressionType, none(): MatchExpressionType }): MatchExpressionType`

```typescript
const fruit = some('Apple')
fruit.match({
  some: (x) => x,
  none: () => 'Banana',
})
// returns 'Apple'
```

```typescript
const fruit = none()
fruit.match({
  some: (x) => x,
  none: () => 'Banana',
})
// returns 'Banana'
```

### isSome

`isSome(): Boolean`

Returns `true` if the `Option` is `some`.

```typescript
const fruit = some('Apple')
fruit.isSome()
// returns `true`
```

```typescript
const fruit = none()
fruit.isSome()
// returns `false`
```

### isNone

`isNone(): Boolean`

Returns `true` if the `Option` is `none`.

```typescript
const fruit = some('Apple')
fruit.isNone()
// returns `false`
```

```typescript
const fruit = none()
fruit.isNone()
// returns `true`
```

### map

`map((DataType) => FnReturnType): Option<FnReturnType>`

Run a function that replaces the value of `some`. Will do nothing on `none`.

```typescript
const fruit = some('Apple')
fruit.map((x) => x.toUpperCase()).unwrapOr('No fruit :(')
// returns 'APPLE'
```

```typescript
const fruit = none()
fruit.map((x) => x.toUpperCase()).unwrapOr('No fruit :(')
// returns 'No fruit :('
```

### mapOr

`mapOr((DataType) => FnReturnType, FnReturnType): FnReturnType`

Run a function that replaces the value of `some`. Will replace `none` with `some` holding `or`.

```typescript
const fruit = some('Apple')
fruit.mapOr((x) => x.toUpperCase(), 'No fruit :(')
// returns 'APPLE'
```

```typescript
const fruit = none()
fruit.mapOr((x) => x.toUpperCase(), 'No fruit :(')
// returns 'No fruit :('
```

### mapOrElse

`mapOrElse((DataType) => FnReturnType, () => FnReturnType): FnReturnType`

Run a function that replaces the value of `some`. Will replace `none` with `some` holding return value of `orElse` function.

```typescript
const fruit = some('Apple')
fruit.mapOr(
  (x) => x.toUpperCase(),
  () => 'No fruit'
)
// returns 'APPLE'
```

```typescript
const fruit = none()
fruit.mapOr(
  (x) => x.toUpperCase(),
  () => 'No fruit'
)
// returns 'No fruit'
```

### filter

`filter((DataType) => Boolean): Option<DataType>`

Returns `none` if `Option` is `none`. Will return `some` with value if `predicate` returns `true`, else it will return `none`.

```typescript
const fruit = some('Apple')

fruit.filter((x) => x === 'Apple').unwrapOr('No fruit')
// returns 'Apple'

fruit.filter((x) => x === 'Banana').unwrapOr('No fruit')
// returns 'No fruit'
```

```typescript
const fruit = none()
fruit.filter((x) => x === 'Apple').unwrapOr('No fruit')
// returns 'No fruit'
```

### zip

`zip(Option<SecondDataType>): Option<[DataType, SecondDataType]>`

Returns a new `Option` with the original value and the value of provided option. If either option is `none` it will return `none`.

```typescript
const fruit = some('Apple')
fruit.zip(some('Banana')).unwrapOr(['No apple', 'No banana'])
// returns ['Apple', 'Banana']
```

```typescript
const fruit = none()
fruit.zip(some('Banana')).unwrapOr(['No apple', 'No banana'])
// returns ['No apple', 'No banana']
```

### and

`and(Option<DataType>): Option<DataType>`

Returns none if any is `none`, else return second option.

```typescript
const fruit = some('Apple')
fruit.and(some('Banana')).unwrapOr('No fruit')
// returns 'Banana'
```

```typescript
const fruit = none()
fruit.and(some('Banana')).unwrapOr('No fruit')
// returns 'No fruit'
```

### andThen

`andThen((DataType) => Option<DataType>): Option<DataType>`

Returns none if any is `none`, else returns result of provided function wrapped in an `Option`. Some languages call this function `flatMap`.

```typescript
const fruit = some('Apple')
fruit.andThen((x) => some(x.toUpperCase())).unwrapOr('No fruit')
// returns 'APPLE'
```

```typescript
const fruit = none()
fruit.andThen((x) => some(x.toUpperCase())).unwrapOr('No fruit')
// returns 'No fruit'
```

### or

`or(Option<DataType>): Option<DataType>`

Returns first `Option` if it is `some`, else provided `Option`.

```typescript
const fruit = some('Apple')
fruit.or(some('Banana')).unwrapOr('No fruit')
// returns 'Apple'
```

```typescript
const fruit = none()
fruit.or(some('Banana')).unwrapOr('No fruit')
// returns 'Banana'
```

```typescript
const fruit = none()
fruit.or(none()).unwrapOr('No fruit')
// returns 'No fruit'
```

### orElse

`orElse(() => Option<DataType>): Option<DataType>`

Returns first `Option` if it is `some`, else returns result of provided function wrapped in an `Option`.

```typescript
const fruit = some('Apple')
fruit.orElse((x) => some(x.toUpperCase())).unwrapOr('No fruit')
// returns 'Apple'
```

```typescript
const fruit = none()
fruit.andThen(() => some('Banana')).unwrapOr('No fruit')
// returns 'BANANA'
```

```typescript
const fruit = none()
fruit.andThen(() => none()).unwrapOr('No fruit')
// returns 'No fruit'
```

### contains

`contains(DataType): boolean`

Returns true if the `Option` is `some` and holds a value equal to the given value.

```typescript
const fruit = some('Apple')
fruit.contains('Apple')
// returns true
```

```typescript
const fruit = none()
fruit.contains('Apple')
// returns false
```

```typescript
const fruit = some('Apple')
fruit.contains('Banana')
// returns false
```

## Maintainers

- [Dennis Wenger](https://gitlab.com/denniswenger10)
