# Optional

The Optional class is an utility class that provides a way to handle potentially null or undefined values in a more concise and expressive manner. It allows wrapping a value in an Optional object, which can then be used to perform various operations on the value, such as checking if it is present, retrieving it, applying transformations, and handling empty values.

Overall, this simple yet powerful class helps create code that’s, simply put, more readable and less error-prone than its procedural counterpart.

## Global Functions

The best way to wrap and starting using optional is through the global utility functions.

### optionalOf

Wraps a value that might be undefined or null in an Optional container.

```typescript
const result1 = optionalOf("Hello");
if (result1.isPresent()) {
  console.log(result1.get()); // Prints: Hello
}

const result2 = optionalOf(null);
if (result2.isEmpty()) {
  console.log("Value is not present"); // Prints: Value is not present
}

const result3 = optionalOf(undefined);
console.log(result3.orElse("Default")); // Prints: Default

const result4 = await optionalOf(user)
  .orElseThrow(() => new HttpError(409, "User doesn't exist"))
  .map(user => {
    return {
      ...user,
      userData,
    };
  })
  .runAsync(user => this.userRepository.save(user));
console.log(result4); // Prints: saved user content
```

### allPresent

Checks if all the elements in an array of Optional objects have a non-empty value.

```typescript
const optional1 = Optional.of(5);
const optional2 = Optional.of(10);
const optional3 = Optional.empty();

const optionals = [optional1, optional2, optional3];

const result = Optional.allPresent(optionals);
console.log(result); // Output: false
```

### anyPresent

Checks if at least one Optional object in an array is present.

```typescript
const optional1 = Optional.of(5);
const optional2 = Optional.empty();
const optional3 = Optional.of(10);

const optionals = [optional1, optional2, optional3];

const result = Optional.anyPresent(optionals);
console.log(result); // true
```

### nonePresent

Checks if none of the Optional objects in the array are present.

```typescript
const optionalsWithValues = [Optional.of("Hello"), Optional.of("World")];
const areNonePresent1 = Optional.nonePresent(optionalsWithValues);
// areNonePresent1: false (both Optionals have values)

const optionalsWithEmptyValues = [Optional.empty(), Optional.empty()];
const areNonePresent2 = Optional.nonePresent(optionalsWithEmptyValues);
// areNonePresent2: true (both Optionals are empty)

const mixedOptionals = [Optional.of("Hello"), Optional.empty()];
const areNonePresent3 = Optional.nonePresent(mixedOptionals);
// areNonePresent3: false (at least one Optional has a value)
```

### coalesce

Returns the first non-empty optional from a list of optionals, or an empty optional if all optionals are empty.

```typescript
const optional1 = Optional.of(5);
const optional2 = Optional.empty();
const optional3 = Optional.of(10);

const result = Optional.coalesce(optional1, optional2, optional3);
console.log(result.get()); // Output: 5
```

## Class Functions

### `of`

The `of` method creates a new `Optional` object with a specified value. It wraps the value, allowing for more concise and expressive code when dealing with potentially null or undefined values.

```typescript
const value = 42;
const optionalValue = Optional.of(value);
```

### `empty`

The `empty` method returns an empty `Optional` object.

```typescript
const emptyOptional = Optional.empty();
```

### `allPresent`

The `allPresent` method checks if all elements in an array of `Optional` objects are present.

```typescript
const optionals = [Optional.of(1), Optional.of(2), Optional.of(3)];
const allPresent = Optional.allPresent(optionals); // true
```

### `anyPresent`

Checks if at least one `Optional` object in an array is present.

```typescript
const optionals = [Optional.of(1), Optional.empty(), Optional.of(3)];
const anyPresent = Optional.anyPresent(optionals); // true
```

### `nonePresent`

The `nonePresent` method checks if none of the `Optional` objects in the array are present.

```typescript
const optionals = [Optional.empty(), Optional.empty(), Optional.empty()];
const nonePresent = Optional.nonePresent(optionals); // true
```

### `coalesce`

Returns the first non-empty `Optional` from a list of optionals, or an empty optional if all optionals are empty.

```typescript
const result = Optional.coalesce(Optional.empty(), Optional.of(42), Optional.empty()); // Optional(42)
```

### `isPresent`

Checks if the value inside the `Optional` object is present.

```typescript
const optionalValue = Optional.of(42);
const isPresent = optionalValue.isPresent(); // true
```

### `ifPresent`

Executes a specified action if the value inside the `Optional` object is present.

```typescript
const optionalValue = Optional.of(42);
optionalValue.ifPresent(value => console.log(`Value is present: ${value}`));
```

### `ifPresentThrow`

Throws an error if the value inside the `Optional` object is present.

```typescript
typescriptCopy codeconst optionalValue = Optional.of(42);
optionalValue.ifPresentThrow(() => new Error('Value is present!'));
```

### `ifThrow`

Throws an error based on a predicate if the value inside the `Optional` object satisfies the predicate.

```typescript
const optionalValue = Optional.of(42);
optionalValue.ifThrow(
  value => value > 50,
  () => new Error('Value is greater than 50!')
);
```

### `if`

Applies a mapper function if the value inside the `Optional` object satisfies a predicate.

```typescript
Value = Optional.of(42);
const result = optionalValue.if(value => value > 30, value => value * 2); // Optional(84)
```

### `get`

Retrieves the value inside the `Optional` object or throws an error if empty.

```typescript
const optionalValue = Optional.of(42);
const value = optionalValue.get(); // 42
```

### `orElse`

Retrieves the value inside the `Optional` object or returns a default value if empty.

```typescript
const optionalValue = Optional.empty();
const result = optionalValue.orElse(100); // 100
```

### **`orElseGet`**

Returns the value of the Optional if present; otherwise, invokes the provided supplier function and returns its result.

```typescript
onst optional = Optional.of("Hello");
const result = optional.orElseGet(() => "Default Value");
console.log(result); // Output: "Hello"

const emptyOptional = Optional.empty();
const defaultValue = emptyOptional.orElseGet(() => "Default Value");
console.log(defaultValue); // Output: "Default Value"
```

### **`orElseThrow`**

Retrieve the value of the Optional object if it is present, or throw an error if the Optional object is empty.

```typescript
const optionalValue = Optional.of("Hello");
const value = optionalValue.orElseThrow(() => new Error("Value is not present"));
console.log(value); // Output: "Hello"

const emptyOptional = Optional.empty();
emptyOptional.orElseThrow(() => new Error("Value is not present")); // Throws an error
```

### **`map`**

Transform the value inside the Optional object using a provided mapper function. It returns a new Optional object with the mapped value. If the original Optional object is empty, it throws an error.

```typescript
const optional = Optional.of(5); // Create an Optional object with a value of 5
const mappedOptional = optional.map(value => value * 2); // Map the value to its double
console.log(mappedOptional.get()); // Output: 10

const emptyOptional = Optional.empty(); // Create an empty Optional object
const defaultValue = 0;
const mappedEmptyOptional = emptyOptional.map(value => value * 2, defaultValue); // Map the value to its double or use the default value if empty
console.log(mappedEmptyOptional.get()); // Output: 0
```

### **`flatMap`**

Apply a mapper function to the value inside the Optional object and return a new Optional object with the mapped value. If the original Optional object is empty, it throws an error.

```typescript
const optional = Optional.of(5);
const mappedOptional = optional.flatMap(value => Optional.of(value * 2));
console.log(mappedOptional.get()); // Output: 10
```

### **`flatMapAsync`**

Chaining asynchronous operations on an optional value. It takes a mapper function that returns a promise of an Optional object. If the current optional value is present, it applies the mapper function and returns the result as an Optional object. If the current optional value is empty, it returns an empty Optional object.

```typescript
const optionalValue = Optional.of(5);
const asyncMapper = (value: number) => {
  return new Promise<Optional<number>>((resolve) => {
    setTimeout(() => {
      resolve(Optional.of(value * 2));
    }, 1000);
  });
};

optionalValue.flatMapAsync(asyncMapper)
  .then((result) => {
    console.log(result.get()); // Output: 10
  })
  .catch((error) => {
    console.error(error);
  });
```

### **`filter`**

Filter the value inside the Optional object based on a given predicate function. It returns a new Optional object containing the filtered value if the original Optional object is present, or an empty Optional object if the original Optional object is not present.

```typescript
const optional = Optional.of([1, 2, 3, 4, 5]); // Create an Optional object with an array value
const filteredOptional = optional.filter(value => value > 3); // Filter the array to keep only values greater than 3
console.log(filteredOptional.get()); // Output: [4, 5]

const emptyOptional = Optional.empty(); // Create an empty Optional object
const filteredEmptyOptional = emptyOptional.filter(value => value > 3); // Filter the empty Optional object
console.log(filteredEmptyOptional.isEmpty()); // Output: true
```

### isEmpty

Checks if the value inside the Optional object is empty or not.

```typescript
const optionalValue = Optional.of("Hello"); // Create an Optional object with a non-empty value
console.log(optionalValue.isEmpty()); // Output: false

const emptyOptional = Optional.empty(); // Create an Optional object with an empty value
console.log(emptyOptional.isEmpty()); // Output: true
```

### **ifEmpty**

Executes a specified action if the value inside the Optional object is empty.

```typescript
const optional = Optional.of(null);
optional.ifEmpty(() => console.log("Value is empty")); // Output: "Value is empty"
```

### ifEmptyThrow

Throws an error if the value inside the Optional object is empty, otherwise it returns the Optional object.

```typescript
const optional = Optional.of("value");
optional.ifEmptyThrow(() => new Error("Value is empty")); // Returns the optional object

const emptyOptional = Optional.empty();
emptyOptional.ifEmptyThrow(() => new Error("Value is empty")); // Throws an error
```

### ifEmptyGet

Returns the value inside the Optional object if it is not empty, otherwise it returns a default value provided by a callback function.

```typescript
const optional = Optional.of("Hello");
const result = optional.ifEmptyGet(() => "Default Value");
console.log(result); // Output: "Hello"

const emptyOptional = Optional.empty();
const defaultValue = emptyOptional.ifEmptyGet(() => "Default Value");
console.log(defaultValue); // Output: "Default Value"
```

### contains

Checks if the value inside the Optional object contains a given search value.

```typescript
const optional = Optional.of([1, 2, 3, 4, 5]);
const searchValue = 3;
const result = optional.contains(searchValue);
console.log(result); // true
```

### every

Checks if a given predicate function returns true for every value inside the Optional object.

```typescript
const optional = Optional.of([1, 2, 3]);
const result = optional.every(value => value > 0);
console.log(result); // true
```

### some

Checks if a given predicate function returns true for at least one value inside the Optional object.

```typescript
const optional = Optional.of([1, 2, 3]);
const result = optional.some(value => value > 0);
console.log(result); // true
```

### match

Checks if the value inside the Optional object matches a given condition.

```typescript
const optional = Optional.of(5);
const isEven = optional.match(value => value % 2 === 0);
console.log(isEven); // true
```

### run

Allows you to execute a callback function on the value stored in the Optional object and return a new Optional object with the result.

```typescript
const optional = Optional.of(5); // Create an Optional object with a value of 5
const newOptional = optional.run(value => value * 2); // Execute the callback function on the value and create a new Optional object with the result
console.log(newOptional.get()); // Output: 10
```

### runAsync

Allows you to asynchronously execute a callback function on the value contained within the Optional object.

```typescript
const optional = Optional.of(5); // Create an Optional object with a value of 5

const asyncCallback = async (value: number) => {
  // Perform some asynchronous operation on the value
  const result = await someAsyncFunction(value);
  return result;
};

const resultPromise = optional.runAsync(asyncCallback); // Execute the async callback on the value

resultPromise.then(result => {
  console.log(result); // Output the result of the asynchronous operation
}).catch(error => {
  console.error(error); // Handle any errors that occurred during the asynchronous operation
});
```

### else

Used to provide an alternative value or action when the optional value is empty.

```typescript
const optionalValue: Optional<number> = Optional.of(5);
const result: Optional<number> = optionalValue.else(() => 10);
console.log(result.get()); // Output: 5

const emptyOptional: Optional<number> = Optional.empty();
const alternativeResult: Optional<number> = emptyOptional.else(() => 10);
console.log(alternativeResult.get()); // Output: 10
```

### elseAsync

Used to execute a callback function asynchronously if the optional value is empty. It returns a promise that resolves to the result of the callback function.

```typescript
const optionalValue: Optional<number> = Optional.of(5);
const result = await optionalValue.elseAsync(() => Promise.resolve(10));
console.log(result); // Output: 5

const emptyOptional: Optional<number> = Optional.empty();
const alternativeResult = await emptyOptional.elseAsync(() => Promise.resolve(10));
console.log(alternativeResult); // Output: 10
```

### equals

Compares the current Optional object with another Optional object for equality.

```typescript
const optional1 = Optional.of(42);
const optional2 = Optional.of(42);
const optional3 = Optional.of(99);

console.log(optional1.equals(optional2)); // true
console.log(optional1.equals(optional3)); // false
console.log(optional1.equals(Optional.empty())); // false
console.log(Optional.empty().equals(Optional.empty())); // true
```
