From 5280cff73e6e99320175e8466a8ff6ebe7105ca4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=81lvarez?= Date: Fri, 4 Oct 2024 13:58:11 +0100 Subject: [PATCH 01/27] feat(try): doc try monad MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Leto Rodríguez Co-authored-by: Borja García --- src/try/try.ts | 89 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/src/try/try.ts b/src/try/try.ts index c3f4a38..9fc0aec 100644 --- a/src/try/try.ts +++ b/src/try/try.ts @@ -1,7 +1,20 @@ import { Monad } from '../monad'; import { Matchable } from '../match'; +/** + * Abstract class representing a computation that may either result in a value or an error. + * @template T The type of the value. + */ abstract class Try implements Monad, Matchable { + /** + * Executes a function and returns a `Try` instance. + * @template T The type of the value. + * @param {() => T} executable The function to execute. + * @returns {Try} A `Success` instance if the function executes without error, otherwise a `Failure` instance. + * @example + * const result = Try.execute(() => JSON.parse('{"key": "value"}')); + * result.match(console.log, error => console.error(error.message)); // { key: 'value' } + */ static execute(executable: () => T): Try { try { return new Success(executable()); @@ -10,6 +23,20 @@ abstract class Try implements Monad, Matchable { } } + /** + * Creates a `Try` instance from a `Matchable` instance. + * @template T The type of the value. + * @param {Matchable} matchable The matchable instance. + * @returns {Try} A `Success` instance if the matchable contains a value, otherwise a `Failure` instance. + * @example + * const some = Option.of(5); + * const success = Try.from(some); + * sucess.match(console.log, error => console.error(error.message)); // 5 + * + * const none = Option.of(undefined); + * const failure = Try.from(none); + * failure.match(console.log, error => console.error(error.message)); // "No error provided" + */ static from(matchable: Matchable): Try { return matchable.match>( (value: T) => new Success(value), @@ -17,18 +44,68 @@ abstract class Try implements Monad, Matchable { ); } + /** + * Transforms the value contained in this `Try` instance. + * @template U The type of the transformed value. + * @param {(value: T) => U} transform The transformation function. + * @returns {Try} A new `Try` instance containing the transformed value. + * @example + * const result = Try.execute(() => 5).map(value => value * 2); + * result.match(console.log, error => console.error(error.message)); // 10 + */ abstract map(transform: (value: T) => U): Try; + /** + * Transforms the value contained in this `Try` instance into another `Try` instance. + * @template U The type of the transformed value. + * @param {(value: T) => Try} transform The transformation function. + * @returns {Try} The result of the transformation function. + * @example + * const result = Try.execute(() => 5).flatMap(value => Try.execute(() => value * 2)); + * result.match(console.log, error => console.error(error.message)); // 10 + */ abstract flatMap(transform: (value: T) => Try): Try; + /** + * Matches the value or error contained in this `Try` instance. + * @template U The type of the result. + * @param {(value: T) => U} ifSuccess The function to call if this is a `Success` instance. + * @param {(error: Error) => U} ifFailure The function to call if this is a `Failure` instance. + * @returns {U} The result of the matching function. + * @example + * const result = Try.execute(() => JSON.parse('invalid json')); + * result.match(console.log, error => console.error(error.message)); // Unexpected token i in JSON at position 0 + */ abstract match(ifSuccess: (value: T) => U, ifFailure: (error: Error) => U): U; + /** + * Checks if this is a `Success` instance. + * @returns {boolean} `true` if this is a `Success` instance, otherwise `false`. + * @example + * const result = Try.execute(() => 5); + * result.match(value => console.log(value.isSuccess()), error => console.error(error.message)); // true + */ abstract isSuccess(): this is Success; + /** + * Checks if this is a `Failure` instance. + * @returns {boolean} `true` if this is a `Failure` instance, otherwise `false`. + * @example + * const result = Try.execute(() => { throw new Error('failure'); }); + * result.match(console.log, error => console.error(error.isFailure()); // true + */ abstract isFailure(): this is Failure; } +/** + * Class representing a successful computation. + * @template T The type of the value. + */ class Success extends Try { + /** + * Creates a new `Success` instance. + * @param {T} value The value of the successful computation. + */ constructor(private value: T) { super(); } @@ -54,11 +131,23 @@ class Success extends Try { } } +/** + * Class representing a failed computation. + * @template T The type of the value. + */ class Failure extends Try { + /** + * Creates a new `Failure` instance. + * @param {Error} error The error of the failed computation. + */ constructor(private error: Error) { super(); } + /** + * A static instance representing a failure with no error provided. + * @type {Failure} + */ static NO_ERROR_PROVIDED = new Failure(new Error('No error provided')); map(_: (_: never) => never): Try { From fd16bdd0823b3eca8302d1dde820967ace3bcf23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=81lvarez?= Date: Fri, 11 Oct 2024 13:08:13 +0100 Subject: [PATCH 02/27] feat(option): doc option monad MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Leto Rodríguez Co-authored-by: Borja García --- src/option/option.ts | 107 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 106 insertions(+), 1 deletion(-) diff --git a/src/option/option.ts b/src/option/option.ts index b769081..0692246 100644 --- a/src/option/option.ts +++ b/src/option/option.ts @@ -2,7 +2,23 @@ import { Nullable } from '../types'; import { Monad } from '../monad'; import { Matchable } from '../match'; +/** + * Abstract class representing an optional value. + * @template T The type of the value. + */ abstract class Option implements Monad, Matchable { + /** + * Creates an `Option` instance from a nullable value. + * @template T The type of the value. + * @param {Nullable} value The nullable value. + * @returns {Option} A `Some` instance if the value is not null or undefined, otherwise a `None` instance. + * @example + * const some = Option.of(5); + * some.match(console.log, () => console.log('none')); // 5 + * + * const none = Option.of(null); + * none.match(console.log, () => console.log('none')); // none + */ static of(value: Nullable): Option { if (value == null) { return new None(); @@ -10,29 +26,114 @@ abstract class Option implements Monad, Matchable { return new Some(value); } - static from(matchable: Matchable) { + /** + * Creates an `Option` instance from a `Matchable` instance. + * @template T The type of the value. + * @param {Matchable} matchable The matchable instance. + * @returns {Option} A `Some` instance if the matchable contains a value, otherwise a `None` instance. + * @example + * const either = Either.right(5); + * const option = Option.from(either); + * option.match(console.log, () => console.log('none')); // 5 + */ + static from(matchable: Matchable): Option { return matchable.match>( (value: T) => Option.of(value), () => Option.of(undefined) ); } + /** + * Returns the value if present, otherwise returns the provided default value. + * @param {T} otherValue The default value. + * @returns {T} The value if present, otherwise the default value. + * @example + * const some = Option.of(5); + * console.log(some.getOrElse(0)); // 5 + * + * const none = Option.of(null); + * console.log(none.getOrElse(0)); // 0 + */ abstract getOrElse(otherValue: T): T; + /** + * Filters the value using a predicate function. + * @param {(value: T) => boolean} predicate The predicate function. + * @returns {Option} A `Some` instance if the value matches the predicate, otherwise a `None` instance. + * @example + * const some = Option.of(5).filter(value => value > 3); + * some.match(console.log, () => console.log('none')); // 5 + * + * const none = Option.of(2).filter(value => value > 3); + * none.match(console.log, () => console.log('none')); // none + */ abstract filter(predicate: (value: T) => boolean): Option; + /** + * Transforms the value contained in this `Option` instance. + * @template U The type of the transformed value. + * @param {(value: T) => U} transform The transformation function. + * @returns {Option} A new `Option` instance containing the transformed value. + * @example + * const some = Option.of(5).map(value => value * 2); + * some.match(console.log, () => console.log('none')); // 10 + */ abstract map(transform: (value: T) => U): Option; + /** + * Transforms the value contained in this `Option` instance into another `Option` instance. + * @template U The type of the transformed value. + * @param {(value: T) => Option} transform The transformation function. + * @returns {Option} The result of the transformation function. + * @example + * const some = Option.of(5).flatMap(value => Option.of(value * 2)); + * some.match(console.log, () => console.log('none')); // 10 + */ abstract flatMap(transform: (value: T) => Option): Option; + /** + * Matches the value or absence of value contained in this `Option` instance. + * @template U The type of the result. + * @param {(value: T) => U} ifSome The function to call if this is a `Some` instance. + * @param {(_: undefined) => U} ifNone The function to call if this is a `None` instance. + * @returns {U} The result of the matching function. + * @example + * const some = Option.of(5); + * some.match(console.log, () => console.log('none')); // 5 + * + * const none = Option.of(null); + * none.match(console.log, () => console.log('none')); // none + */ abstract match(ifSome: (value: T) => U, ifNone: (_: undefined) => U): U; + /** + * Checks if this is a `Some` instance. + * @returns {boolean} `true` if this is a `Some` instance, otherwise `false`. + * @example + * const some = Option.of(5); + * some.match(value => console.log(value.isSome()), () => console.log('none')); // true + */ abstract isSome(): this is Some; + /** + * Checks if this is a `None` instance. + * @returns {boolean} `true` if this is a `None` instance, otherwise `false`. + * @example + * const none = Option.of(null); + * none.match(console.log, none => console.log(none.isNone())); // true + */ abstract isNone(): this is None; } +/** + * Class representing an optional value that is present. + * @template T The type of the value. + */ class Some extends Option { + /** + * Creates a new `Some` instance. + * @param {T} value The value of the optional. + */ constructor(private value: T) { super(); } @@ -66,6 +167,10 @@ class Some extends Option { } } +/** + * Class representing an optional value that is absent. + * @template T The type of the value. + */ class None extends Option { getOrElse(value: T): T { return value; From 30f0f23188c07a7c1b8534a2e143da0a4c0eb299 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=81lvarez?= Date: Fri, 11 Oct 2024 13:39:24 +0100 Subject: [PATCH 03/27] feat(io): doc io monad MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Leto Rodríguez Co-authored-by: Borja García --- src/io/io.ts | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/io/io.ts b/src/io/io.ts index 8543122..da36d82 100644 --- a/src/io/io.ts +++ b/src/io/io.ts @@ -1,19 +1,63 @@ import { Monad } from '../monad'; +/** + * Class representing an IO monad, which encapsulates a side-effectful computation description. + * @template T The type of the value produced by the IO computation. + */ class IO implements Monad { + /** + * Creates a new `IO` instance. + * @param {() => T} description The side-effectful computation. + * @private + */ private constructor(private description: () => T) {} + /** + * Creates an `IO` instance from a side-effectful computation. + * @template T The type of the value produced by the IO computation. + * @param {() => T} sideEffect The side-effectful computation. + * @returns {IO} A new `IO` instance. + * @example + * const io = IO.of(() => console.log('Hello, World!')); + * io.runUnsafe(); // Hello, World! + */ static of(sideEffect: () => T): IO { return new IO(sideEffect); } + /** + * Transforms the value produced by this `IO` instance into another `IO` instance. + * @template U The type of the transformed value. + * @param {(value: T) => IO} transform The transformation function. + * @returns {IO} A new `IO` instance containing the transformed value. + * @example + * const io = IO.of(() => 5).flatMap(value => IO.of(() => value * 2)); + * console.log(io.runUnsafe()); // 10 + */ flatMap(transform: (value: T) => IO): IO { return new IO(() => transform(this.description()).runUnsafe()); } + + /** + * Transforms the value produced by this `IO` instance. + * @template U The type of the transformed value. + * @param {(value: T) => U} transform The transformation function. + * @returns {IO} A new `IO` instance containing the transformed value. + * @example + * const io = IO.of(() => 5).map(value => value * 2); + * console.log(io.runUnsafe()); // 10 + */ map(transform: (value: T) => U): IO { return new IO(() => transform(this.description())); } + /** + * Executes the side-effectful computation and returns the result. + * @returns {T} The result of the side-effectful computation. + * @example + * const io = IO.of(() => 5); + * console.log(io.runUnsafe()); // 5 + */ runUnsafe(): T { return this.description(); } From e9208b48ff79a3891fd92570649e8d3a3359e729 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=81lvarez?= Date: Fri, 11 Oct 2024 16:05:26 +0100 Subject: [PATCH 04/27] feat(either): doc either monad MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Leto Rodríguez Co-authored-by: Borja García --- src/either/either.ts | 121 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/src/either/either.ts b/src/either/either.ts index 7e025bd..e2294dc 100644 --- a/src/either/either.ts +++ b/src/either/either.ts @@ -1,15 +1,49 @@ import { Monad } from '../monad'; import { Matchable } from '../match'; +/** + * Abstract class representing a value that can be one of two possible types. + * @template L The type of the left value. + * @template R The type of the right value. + */ abstract class Either implements Monad, Matchable { + /** + * Creates a `Right` instance. + * @template T The type of the right value. + * @param {T} value The right value. + * @returns {Either} A `Right` instance containing the value. + * @example + * const right = Either.right(5); + * right.match(console.log, error => console.error(error.message)); // 5 + */ static right(value: T): Either { return new Right(value); } + /** + * Creates a `Left` instance. + * @template T The type of the left value. + * @param {T} value The left value. + * @returns {Either} A `Left` instance containing the value. + * @example + * const left = Either.left('error'); + * left.match(console.log, error => console.error(error.message)); // 'error' + */ static left(value: T): Either { return new Left(value); } + /** + * Creates an `Either` instance from a `Matchable` instance. + * @template L The type of the left value. + * @template R The type of the right value. + * @param {Matchable} matchable The matchable instance. + * @returns {Either} A `Right` instance if the matchable contains a right value, otherwise a `Left` instance. + * @example + * const option = Option.of(5); + * const either = Either.from(option); + * either.match(console.log, error => console.error(error.message)); // 5 + */ static from(matchable: Matchable): Either { return matchable.match>( (value: R) => Either.right(value), @@ -17,6 +51,15 @@ abstract class Either implements Monad, Matchable { ); } + /** + * Executes a function and returns an `Either` instance. + * @template T The type of the right value. + * @param {() => T} execute The function to execute. + * @returns {Either} A `Right` instance if the function executes without error, otherwise a `Left` instance. + * @example + * const result = Either.catch(() => JSON.parse('invalid json')); + * result.match(console.log, error => console.error(error.message)); // 'SyntaxError: Unexpected token i in JSON at position 0' + */ static catch(execute: () => T): Either { try { return Either.right(execute()); @@ -25,22 +68,91 @@ abstract class Either implements Monad, Matchable { } } + /** + * Transforms the right value contained in this `Either` instance. + * @template T The type of the transformed value. + * @param {(r: R) => T} transform The transformation function. + * @returns {Either} A new `Either` instance containing the transformed value. + * @example + * const result = Either.right(5).map(value => value * 2); + * result.match(console.log, console.error); // 10 + */ abstract map(transform: (r: R) => T): Either; + /** + * Transforms the left value contained in this `Either` instance. + * @template T The type of the transformed value. + * @param {(l: L) => T} transform The transformation function. + * @returns {Either} A new `Either` instance containing the transformed value. + * @example + * const result = Either.left('error').mapLeft(value => `Error: ${value}`); + * result.match(console.log, console.error); // 'Error: error' + */ abstract mapLeft(transform: (l: L) => T): Either; + /** + * Transforms the right value contained in this `Either` instance into another `Either` instance. + * @template T The type of the transformed value. + * @param {(r: R) => Either} transform The transformation function. + * @returns {Either} The result of the transformation function. + * @example + * const result = Either.right(5).flatMap(value => Either.right(value * 2)); + * result.match(console.log, console.error); // 10 + */ abstract flatMap(transform: (r: R) => Either): Either; + /** + * Transforms the left value contained in this `Either` instance into another `Either` instance. + * @template T The type of the transformed value. + * @param {(l: L) => Either} transform The transformation function. + * @returns {Either} The result of the transformation function. + * @example + * const result = Either.left('error').flatMapLeft(value => Either.left(`Error: ${value}`)); + * result.match(console.log, console.error); // Error: error + */ abstract flatMapLeft(transform: (l: L) => Either): Either; + /** + * Matches the right or left value contained in this `Either` instance. + * @template T The type of the result. + * @param {(r: R) => T} ifRight The function to call if this is a `Right` instance. + * @param {(l: L) => T} ifLeft The function to call if this is a `Left` instance. + * @returns {T} The result of the matching function. + * @example + * const result = Either.right(5); + * result.match(console.log, console.error); // 5 + */ abstract match(ifRight: (r: R) => T, ifLeft: (l: L) => T): T; + /** + * Checks if this is a `Left` instance. + * @returns {boolean} `true` if this is a `Left` instance, otherwise `false`. + * @example + * const result = Either.left('error'); + * console.log(result.isLeft()); // true + */ abstract isLeft(): this is Left; + /** + * Checks if this is a `Right` instance. + * @returns {boolean} `true` if this is a `Right` instance, otherwise `false`. + * @example + * const result = Either.right(5); + * console.log(result.isRight()); // true + */ abstract isRight(): this is Right; } +/** + * Class representing a left value. + * @template L The type of the left value. + * @template R The type of the right value. + */ class Left extends Either { + /** + * Creates a new `Left` instance. + * @param {L} value The left value. + */ constructor(private value: L) { super(); } @@ -74,7 +186,16 @@ class Left extends Either { } } +/** + * Class representing a right value. + * @template L The type of the left value. + * @template R The type of the right value. + */ class Right extends Either { + /** + * Creates a new `Right` instance. + * @param {R} value The right value. + */ constructor(private value: R) { super(); } From a77b7f8cc3a9eed18f572af31c608bffb6122af2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=81lvarez?= Date: Fri, 11 Oct 2024 16:16:33 +0100 Subject: [PATCH 05/27] feat(future): doc future monad MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Leto Rodríguez Co-authored-by: Borja García --- src/future/future.ts | 46 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/future/future.ts b/src/future/future.ts index f3a613e..5ad8dda 100644 --- a/src/future/future.ts +++ b/src/future/future.ts @@ -1,21 +1,67 @@ import { Monad } from '../monad'; import { Completable } from '../complete'; +/** + * Class representing a future computation that will produce a value or an error. + * @template T The type of the value produced by the future computation. + */ class Future implements Monad, Completable { + /** + * Creates a new `Future` instance. + * @param {() => Promise} action The asynchronous action to be performed. + * @private + */ private constructor(private readonly action: () => Promise) {} + /** + * Creates a `Future` instance from an asynchronous action. + * @template T The type of the value produced by the future computation. + * @param {() => Promise} action The asynchronous action to be performed. + * @returns {Future} A new `Future` instance. + * @example + * const future = Future.of(() => Promise.resolve(5)); + * future.complete(console.log, error => console.error(error.message)); // 5 + */ static of(action: () => Promise): Future { return new Future(action); } + /** + * Transforms the value produced by this `Future` instance. + * @template U The type of the transformed value. + * @param {(value: T) => U} transform The transformation function. + * @returns {Future} A new `Future` instance containing the transformed value. + * @example + * const future = Future.of(() => Promise.resolve(5)).map(value => value * 2); + * future.complete(console.log, error => console.error(error.message)); // 10 + */ map(transform: (value: T) => U): Future { return new Future(() => this.action().then(transform)); } + /** + * Transforms the value produced by this `Future` instance into another `Future` instance. + * @template U The type of the transformed value. + * @param {(value: T) => Future} transform The transformation function. + * @returns {Future} A new `Future` instance containing the transformed value. + * @example + * const future = Future.of(() => Promise.resolve(5)).flatMap(value => Future.of(() => Promise.resolve(value * 2))); + * future.complete(console.log, error => console.error(error.message)); // 10 + */ flatMap(transform: (value: T) => Future): Future { return new Future(() => this.action().then((value) => transform(value).action())); } + /** + * Completes the future computation by executing the provided success or failure handlers. + * @template S The type of the result produced by the handlers. + * @param {(value: T) => S} onSuccess The function to call if the computation succeeds. + * @param {(error: Error) => S} onFailure The function to call if the computation fails. + * @returns {Promise} A promise that resolves to the result of the handlers. + * @example + * const future = Future.of(() => Promise.resolve(5)); + * future.complete(console.log, error => console.error(error.message)); // 5 + */ complete(onSuccess: (value: T) => S, onFailure: (error: Error) => S): Promise { return this.action().then(onSuccess).catch(onFailure); } From 14a63493418a06465e7930c943fbcb792baae042 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20=C3=81lvarez?= Date: Fri, 18 Oct 2024 12:26:36 +0100 Subject: [PATCH 06/27] refactor: apply suggested changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Leto Rodríguez Co-authored-by: Borja García --- src/either/either.ts | 4 ++-- src/io/io.ts | 12 ++++++------ src/option/option.ts | 2 +- src/try/try.ts | 2 +- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/either/either.ts b/src/either/either.ts index e2294dc..4d50be5 100644 --- a/src/either/either.ts +++ b/src/either/either.ts @@ -52,7 +52,7 @@ abstract class Either implements Monad, Matchable { } /** - * Executes a function and returns an `Either` instance. + * Executes a function and returns an `Either` instance that can raise an Error. * @template T The type of the right value. * @param {() => T} execute The function to execute. * @returns {Either} A `Right` instance if the function executes without error, otherwise a `Left` instance. @@ -113,7 +113,7 @@ abstract class Either implements Monad, Matchable { abstract flatMapLeft(transform: (l: L) => Either): Either; /** - * Matches the right or left value contained in this `Either` instance. + * Unwraps the value contained in this `Either` instance by applying the appropriate handler for both Left and Right cases. * @template T The type of the result. * @param {(r: R) => T} ifRight The function to call if this is a `Right` instance. * @param {(l: L) => T} ifLeft The function to call if this is a `Left` instance. diff --git a/src/io/io.ts b/src/io/io.ts index da36d82..5ed68de 100644 --- a/src/io/io.ts +++ b/src/io/io.ts @@ -1,21 +1,21 @@ import { Monad } from '../monad'; /** - * Class representing an IO monad, which encapsulates a side-effectful computation description. + * Class representing an IO monad, which encapsulates a computation description that may produce a side effect. * @template T The type of the value produced by the IO computation. */ class IO implements Monad { /** * Creates a new `IO` instance. - * @param {() => T} description The side-effectful computation. + * @param {() => T} description The computation. * @private */ private constructor(private description: () => T) {} /** - * Creates an `IO` instance from a side-effectful computation. + * Creates an `IO` instance from a computation that may produce a side effect. * @template T The type of the value produced by the IO computation. - * @param {() => T} sideEffect The side-effectful computation. + * @param {() => T} sideEffect The computation. * @returns {IO} A new `IO` instance. * @example * const io = IO.of(() => console.log('Hello, World!')); @@ -52,8 +52,8 @@ class IO implements Monad { } /** - * Executes the side-effectful computation and returns the result. - * @returns {T} The result of the side-effectful computation. + * Executes the computation and returns the result. + * @returns {T} The result of the computation. * @example * const io = IO.of(() => 5); * console.log(io.runUnsafe()); // 5 diff --git a/src/option/option.ts b/src/option/option.ts index 0692246..076836e 100644 --- a/src/option/option.ts +++ b/src/option/option.ts @@ -92,7 +92,7 @@ abstract class Option implements Monad, Matchable { abstract flatMap(transform: (value: T) => Option): Option; /** - * Matches the value or absence of value contained in this `Option` instance. + * Unwraps the value contained in this `Option` instance by applying the appropriate handler for both Some and None cases. * @template U The type of the result. * @param {(value: T) => U} ifSome The function to call if this is a `Some` instance. * @param {(_: undefined) => U} ifNone The function to call if this is a `None` instance. diff --git a/src/try/try.ts b/src/try/try.ts index 9fc0aec..5605e7e 100644 --- a/src/try/try.ts +++ b/src/try/try.ts @@ -67,7 +67,7 @@ abstract class Try implements Monad, Matchable { abstract flatMap(transform: (value: T) => Try): Try; /** - * Matches the value or error contained in this `Try` instance. + * Unwraps the value contained in this `Try` instance by applying the appropriate handler for both Success and Failure cases. * @template U The type of the result. * @param {(value: T) => U} ifSuccess The function to call if this is a `Success` instance. * @param {(error: Error) => U} ifFailure The function to call if this is a `Failure` instance. From 5126ce212e3933764afb966e39d2bc0b7b5728cf Mon Sep 17 00:00:00 2001 From: Aitor Santana Date: Sun, 27 Oct 2024 19:16:23 +0000 Subject: [PATCH 07/27] feat(Option): :rotating_light: Option monad should create a Some --- src/option/option.test.ts | 5 +++++ src/option/option.ts | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/src/option/option.test.ts b/src/option/option.test.ts index 336ef06..5b9254d 100644 --- a/src/option/option.test.ts +++ b/src/option/option.test.ts @@ -49,4 +49,9 @@ describe('Option monad', () => { ])('$type should handle isSome operation correctly', ({ option, expected }) => { expect(option.isSome()).toEqual(expected); }); + + it('should create a Some', () => { + const option = Option.ofSome(2); + expect(option).toEqual(new Some(2)); + }); }); diff --git a/src/option/option.ts b/src/option/option.ts index 076836e..9d5d645 100644 --- a/src/option/option.ts +++ b/src/option/option.ts @@ -123,6 +123,10 @@ abstract class Option implements Monad, Matchable { * none.match(console.log, none => console.log(none.isNone())); // true */ abstract isNone(): this is None; + + static ofSome(number: number) { + + } } /** From c19cf75688fa81c6d00eb687c84293010e06d899 Mon Sep 17 00:00:00 2001 From: Aitor Santana Date: Sun, 27 Oct 2024 19:18:02 +0000 Subject: [PATCH 08/27] feat(Option): :white_check_mark: Option monad should create a Some --- src/option/option.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/option/option.ts b/src/option/option.ts index 9d5d645..ca9129d 100644 --- a/src/option/option.ts +++ b/src/option/option.ts @@ -26,6 +26,10 @@ abstract class Option implements Monad, Matchable { return new Some(value); } + static ofSome(value: T) { + return new Some(value); + } + /** * Creates an `Option` instance from a `Matchable` instance. * @template T The type of the value. @@ -123,10 +127,6 @@ abstract class Option implements Monad, Matchable { * none.match(console.log, none => console.log(none.isNone())); // true */ abstract isNone(): this is None; - - static ofSome(number: number) { - - } } /** From 2f22faf6349aed9bf381533ce832e21f1c3ffc64 Mon Sep 17 00:00:00 2001 From: Aitor Santana Date: Sun, 27 Oct 2024 19:20:37 +0000 Subject: [PATCH 09/27] feat(Option): :rotating_light: Option monad should create a None --- src/option/option.test.ts | 7 +++++-- src/option/option.ts | 4 ++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/option/option.test.ts b/src/option/option.test.ts index 5b9254d..0be6266 100644 --- a/src/option/option.test.ts +++ b/src/option/option.test.ts @@ -51,7 +51,10 @@ describe('Option monad', () => { }); it('should create a Some', () => { - const option = Option.ofSome(2); - expect(option).toEqual(new Some(2)); + expect(Option.ofSome(2)).toEqual(new Some(2)); + }); + + it('should create a None', () => { + expect(Option.ofNone()).toEqual(new None()); }); }); diff --git a/src/option/option.ts b/src/option/option.ts index ca9129d..217ee29 100644 --- a/src/option/option.ts +++ b/src/option/option.ts @@ -127,6 +127,10 @@ abstract class Option implements Monad, Matchable { * none.match(console.log, none => console.log(none.isNone())); // true */ abstract isNone(): this is None; + + static ofNone() { + return null; + } } /** From 929b72a67afcda853bdd77e2be7eac9d9567dd48 Mon Sep 17 00:00:00 2001 From: Aitor Santana Date: Sun, 27 Oct 2024 19:21:00 +0000 Subject: [PATCH 10/27] feat(Option): :rotating_light: Option monad should create a None --- src/option/option.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/option/option.ts b/src/option/option.ts index 217ee29..c0e1d89 100644 --- a/src/option/option.ts +++ b/src/option/option.ts @@ -129,7 +129,7 @@ abstract class Option implements Monad, Matchable { abstract isNone(): this is None; static ofNone() { - return null; + return new None(); } } From 37c5e25a186ac23d2f2ff5d34771591f1845e28a Mon Sep 17 00:00:00 2001 From: Aitor Santana Date: Sun, 27 Oct 2024 19:24:04 +0000 Subject: [PATCH 11/27] feat(Option): :white_check_mark: Option monad should create a None --- src/option/option.ts | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/option/option.ts b/src/option/option.ts index c0e1d89..e4bd7ea 100644 --- a/src/option/option.ts +++ b/src/option/option.ts @@ -26,10 +26,30 @@ abstract class Option implements Monad, Matchable { return new Some(value); } - static ofSome(value: T) { + /** + * Creates an `Option` instance from a value. + * @template T The type of the value. + * @param {Nullable} value The nullable value. + * @returns {Some} A `Some` instance of the value + * @example + * const some = Option.ofSome(5); + * some.match(console.log, () => console.log('none')); // 5 + */ + static ofSome(value: T): Some { return new Some(value); } + /** + * Creates a `None` instance. + * @returns {None} A `None` instance. + * @example + * const none = Option.ofNone(); + * none.match(console.log, () => console.log('none')); // none + */ + static ofNone(): None { + return new None(); + } + /** * Creates an `Option` instance from a `Matchable` instance. * @template T The type of the value. @@ -127,10 +147,6 @@ abstract class Option implements Monad, Matchable { * none.match(console.log, none => console.log(none.isNone())); // true */ abstract isNone(): this is None; - - static ofNone() { - return new None(); - } } /** From 1f6461d861082a945d5dbe4fabff915701953c94 Mon Sep 17 00:00:00 2001 From: Aitor Santana Date: Sun, 27 Oct 2024 23:11:58 +0000 Subject: [PATCH 12/27] feat(Option): :recycle: Add NotNullable type --- src/option/option.test.ts | 1 + src/option/option.ts | 8 ++++---- src/types.ts | 2 ++ tsconfig.json | 4 +++- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/option/option.test.ts b/src/option/option.test.ts index 0be6266..2907acf 100644 --- a/src/option/option.test.ts +++ b/src/option/option.test.ts @@ -52,6 +52,7 @@ describe('Option monad', () => { it('should create a Some', () => { expect(Option.ofSome(2)).toEqual(new Some(2)); + expect(Option.ofSome(null)).toEqual(new Some(null)); }); it('should create a None', () => { diff --git a/src/option/option.ts b/src/option/option.ts index e4bd7ea..3209807 100644 --- a/src/option/option.ts +++ b/src/option/option.ts @@ -1,4 +1,4 @@ -import { Nullable } from '../types'; +import { NotNullable, Nullable } from '../types'; import { Monad } from '../monad'; import { Matchable } from '../match'; @@ -29,13 +29,13 @@ abstract class Option implements Monad, Matchable { /** * Creates an `Option` instance from a value. * @template T The type of the value. - * @param {Nullable} value The nullable value. + * @param {NotNullable} value The nullable value. * @returns {Some} A `Some` instance of the value * @example * const some = Option.ofSome(5); * some.match(console.log, () => console.log('none')); // 5 */ - static ofSome(value: T): Some { + static ofSome(value: NotNullable): Option { return new Some(value); } @@ -46,7 +46,7 @@ abstract class Option implements Monad, Matchable { * const none = Option.ofNone(); * none.match(console.log, () => console.log('none')); // none */ - static ofNone(): None { + static ofNone(): Option { return new None(); } diff --git a/src/types.ts b/src/types.ts index 164a705..8bde8f6 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1 +1,3 @@ export type Nullable = T | null | undefined; +//type NotNullable = T extends null | undefined ? never : T +export type NotNullable = Exclude; diff --git a/tsconfig.json b/tsconfig.json index 400cf54..7cd88b8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,7 +8,9 @@ "moduleResolution": "node", "esModuleInterop": true, "skipLibCheck": true, - "forceConsistentCasingInFileNames": true + "forceConsistentCasingInFileNames": true, + "strict": true, + "strictNullChecks": true, }, "include": ["src"], "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"] From 7d7dda66ef18abbef8b36f93ba556843cd3655c0 Mon Sep 17 00:00:00 2001 From: Aitor Santana Date: Mon, 28 Oct 2024 21:13:05 +0000 Subject: [PATCH 13/27] feat(Option): :fire: Remove unnecessary test --- src/option/option.test.ts | 1 - src/types.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/option/option.test.ts b/src/option/option.test.ts index 2907acf..0be6266 100644 --- a/src/option/option.test.ts +++ b/src/option/option.test.ts @@ -52,7 +52,6 @@ describe('Option monad', () => { it('should create a Some', () => { expect(Option.ofSome(2)).toEqual(new Some(2)); - expect(Option.ofSome(null)).toEqual(new Some(null)); }); it('should create a None', () => { diff --git a/src/types.ts b/src/types.ts index 8bde8f6..41acb98 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,3 +1,2 @@ export type Nullable = T | null | undefined; -//type NotNullable = T extends null | undefined ? never : T export type NotNullable = Exclude; From 2cd903481f70b6f16622d8b38c11a51f6077bd93 Mon Sep 17 00:00:00 2001 From: Aitor Santana Date: Tue, 29 Oct 2024 08:01:58 +0000 Subject: [PATCH 14/27] feat(Option): :recycle: Rename options methods --- src/option/option.test.ts | 4 ++-- src/option/option.ts | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/option/option.test.ts b/src/option/option.test.ts index 0be6266..0899fda 100644 --- a/src/option/option.test.ts +++ b/src/option/option.test.ts @@ -51,10 +51,10 @@ describe('Option monad', () => { }); it('should create a Some', () => { - expect(Option.ofSome(2)).toEqual(new Some(2)); + expect(Option.some(2)).toEqual(new Some(2)); }); it('should create a None', () => { - expect(Option.ofNone()).toEqual(new None()); + expect(Option.none()).toEqual(new None()); }); }); diff --git a/src/option/option.ts b/src/option/option.ts index 3209807..d5f65fe 100644 --- a/src/option/option.ts +++ b/src/option/option.ts @@ -32,10 +32,10 @@ abstract class Option implements Monad, Matchable { * @param {NotNullable} value The nullable value. * @returns {Some} A `Some` instance of the value * @example - * const some = Option.ofSome(5); + * const some = Option.some(5); * some.match(console.log, () => console.log('none')); // 5 */ - static ofSome(value: NotNullable): Option { + static some(value: NotNullable): Option { return new Some(value); } @@ -43,10 +43,10 @@ abstract class Option implements Monad, Matchable { * Creates a `None` instance. * @returns {None} A `None` instance. * @example - * const none = Option.ofNone(); + * const none = Option.none(); * none.match(console.log, () => console.log('none')); // none */ - static ofNone(): Option { + static none(): Option { return new None(); } From e1b99b943dcd567d97dbf42732151a9b2cbd2175 Mon Sep 17 00:00:00 2001 From: Aitor Santana Date: Thu, 31 Oct 2024 09:15:46 +0000 Subject: [PATCH 15/27] feat(Option): :recycle: Rename NotNullabe type to Present --- src/option/option.ts | 6 +++--- src/types.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/option/option.ts b/src/option/option.ts index d5f65fe..e997286 100644 --- a/src/option/option.ts +++ b/src/option/option.ts @@ -1,4 +1,4 @@ -import { NotNullable, Nullable } from '../types'; +import { Present, Nullable } from '../types'; import { Monad } from '../monad'; import { Matchable } from '../match'; @@ -29,13 +29,13 @@ abstract class Option implements Monad, Matchable { /** * Creates an `Option` instance from a value. * @template T The type of the value. - * @param {NotNullable} value The nullable value. + * @param {Present} value The nullable value. * @returns {Some} A `Some` instance of the value * @example * const some = Option.some(5); * some.match(console.log, () => console.log('none')); // 5 */ - static some(value: NotNullable): Option { + static some(value: Present): Option { return new Some(value); } diff --git a/src/types.ts b/src/types.ts index 41acb98..31161a2 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,2 +1,2 @@ export type Nullable = T | null | undefined; -export type NotNullable = Exclude; +export type Present = Exclude; From 0aa808a4b869f19d554b6127908bcdc2b86ec187 Mon Sep 17 00:00:00 2001 From: Mario Pinto Date: Fri, 1 Nov 2024 16:42:54 +0000 Subject: [PATCH 16/27] fix(Try): fix Failure subclass generic typing and remove static variable --- src/try/try.test.ts | 2 +- src/try/try.ts | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/try/try.test.ts b/src/try/try.test.ts index d3f5088..7611be2 100644 --- a/src/try/try.test.ts +++ b/src/try/try.test.ts @@ -35,7 +35,7 @@ describe('Try monad', () => { typeMatchable: 'None', tryType: 'Failure', matchable: Option.of(undefined), - expected: Failure.NO_ERROR_PROVIDED, + expected: new Failure(new Error('No error provided')), }, ])('$tryType should be created from $typeMatchable', ({ matchable, expected }) => { expect(Try.from(matchable)).toEqual(expected); diff --git a/src/try/try.ts b/src/try/try.ts index 5605e7e..170cbb1 100644 --- a/src/try/try.ts +++ b/src/try/try.ts @@ -40,7 +40,7 @@ abstract class Try implements Monad, Matchable { static from(matchable: Matchable): Try { return matchable.match>( (value: T) => new Success(value), - (error: unknown) => (error instanceof Error ? new Failure(error) : Failure.NO_ERROR_PROVIDED) + (error: unknown) => (error instanceof Error ? new Failure(error) : new Failure(new Error('No error provided'))) ); } @@ -135,7 +135,7 @@ class Success extends Try { * Class representing a failed computation. * @template T The type of the value. */ -class Failure extends Try { +class Failure extends Try { /** * Creates a new `Failure` instance. * @param {Error} error The error of the failed computation. @@ -148,7 +148,6 @@ class Failure extends Try { * A static instance representing a failure with no error provided. * @type {Failure} */ - static NO_ERROR_PROVIDED = new Failure(new Error('No error provided')); map(_: (_: never) => never): Try { return new Failure(this.error); From fcd283932048ae358990973ae0af1802e9bd4eab Mon Sep 17 00:00:00 2001 From: Mario Pinto Date: Fri, 1 Nov 2024 16:43:52 +0000 Subject: [PATCH 17/27] fix(monads): fix compilation to enable commonjs and esm entrypoint to be used in any project --- package.json | 8 +++++--- tsconfig.json | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index fc2ecdd..b28c3b7 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "@leanmind/monads", - "main": "dist/index.js", - "module": "dist/index.esm.js", + "main": "dist/cjs/index.js", + "module": "dist/esm/index.js", "types": "dist/index.d.ts", "version": "0.0.0-semantically-released", "description": "A collection of monads implemented in TypeScript using object-oriented programming.", @@ -13,7 +13,9 @@ ], "scripts": { "analize": "npm run lint:fix && npm run compile", - "build": "npm run lint:fix && tsc", + "build": "npm run lint:fix && npm run compile:commonjs && npm run compile:esm", + "compile:commonjs": "tsc --outDir dist/cjs --module commonjs", + "compile:esm": "tsc --outDir dist/esm --module esnext", "compile": "tsc --noEmit", "compile:watch": "npm run compile -- --watch", "compile:build": "tsc -b", diff --git a/tsconfig.json b/tsconfig.json index 400cf54..e4e6cd5 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,6 +1,7 @@ { "compilerOptions": { "declaration": true, + "declarationDir": "./dist", "outDir": "./dist", "module": "ESNext", "target": "ESNext", From f024f324a56c25af9c36b04ac5a1c13ab15c7b6a Mon Sep 17 00:00:00 2001 From: Mario Pinto Date: Fri, 1 Nov 2024 17:42:34 +0000 Subject: [PATCH 18/27] docs(monads): add tag --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index b28c3b7..ddba967 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,8 @@ "functional-programming", "monads", "typescript", - "oop" + "oop", + "error-handling" ], "scripts": { "analize": "npm run lint:fix && npm run compile", From ffcd133afb665ee62357e83be7d1df6648994648 Mon Sep 17 00:00:00 2001 From: Aitor Santana Date: Sun, 27 Oct 2024 19:16:23 +0000 Subject: [PATCH 19/27] feat(Option): :rotating_light: Option monad should create a Some --- src/option/option.test.ts | 5 +++++ src/option/option.ts | 4 ++++ 2 files changed, 9 insertions(+) diff --git a/src/option/option.test.ts b/src/option/option.test.ts index 336ef06..5b9254d 100644 --- a/src/option/option.test.ts +++ b/src/option/option.test.ts @@ -49,4 +49,9 @@ describe('Option monad', () => { ])('$type should handle isSome operation correctly', ({ option, expected }) => { expect(option.isSome()).toEqual(expected); }); + + it('should create a Some', () => { + const option = Option.ofSome(2); + expect(option).toEqual(new Some(2)); + }); }); diff --git a/src/option/option.ts b/src/option/option.ts index 076836e..9d5d645 100644 --- a/src/option/option.ts +++ b/src/option/option.ts @@ -123,6 +123,10 @@ abstract class Option implements Monad, Matchable { * none.match(console.log, none => console.log(none.isNone())); // true */ abstract isNone(): this is None; + + static ofSome(number: number) { + + } } /** From 83e23a1d17772e32537523e3939517ccc40c3026 Mon Sep 17 00:00:00 2001 From: Aitor Santana Date: Sun, 27 Oct 2024 19:18:02 +0000 Subject: [PATCH 20/27] feat(Option): :white_check_mark: Option monad should create a Some --- src/option/option.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/option/option.ts b/src/option/option.ts index 9d5d645..ca9129d 100644 --- a/src/option/option.ts +++ b/src/option/option.ts @@ -26,6 +26,10 @@ abstract class Option implements Monad, Matchable { return new Some(value); } + static ofSome(value: T) { + return new Some(value); + } + /** * Creates an `Option` instance from a `Matchable` instance. * @template T The type of the value. @@ -123,10 +127,6 @@ abstract class Option implements Monad, Matchable { * none.match(console.log, none => console.log(none.isNone())); // true */ abstract isNone(): this is None; - - static ofSome(number: number) { - - } } /** From bc32c8a661c92599efbced30ca795eca64b0d32e Mon Sep 17 00:00:00 2001 From: Aitor Santana Date: Sun, 27 Oct 2024 19:20:37 +0000 Subject: [PATCH 21/27] feat(Option): :rotating_light: Option monad should create a None --- src/option/option.test.ts | 7 +++++-- src/option/option.ts | 4 ++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/option/option.test.ts b/src/option/option.test.ts index 5b9254d..0be6266 100644 --- a/src/option/option.test.ts +++ b/src/option/option.test.ts @@ -51,7 +51,10 @@ describe('Option monad', () => { }); it('should create a Some', () => { - const option = Option.ofSome(2); - expect(option).toEqual(new Some(2)); + expect(Option.ofSome(2)).toEqual(new Some(2)); + }); + + it('should create a None', () => { + expect(Option.ofNone()).toEqual(new None()); }); }); diff --git a/src/option/option.ts b/src/option/option.ts index ca9129d..217ee29 100644 --- a/src/option/option.ts +++ b/src/option/option.ts @@ -127,6 +127,10 @@ abstract class Option implements Monad, Matchable { * none.match(console.log, none => console.log(none.isNone())); // true */ abstract isNone(): this is None; + + static ofNone() { + return null; + } } /** From 0953d2a3e80886aa90e1d424f2393d0a2b502910 Mon Sep 17 00:00:00 2001 From: Aitor Santana Date: Sun, 27 Oct 2024 19:21:00 +0000 Subject: [PATCH 22/27] feat(Option): :rotating_light: Option monad should create a None --- src/option/option.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/option/option.ts b/src/option/option.ts index 217ee29..c0e1d89 100644 --- a/src/option/option.ts +++ b/src/option/option.ts @@ -129,7 +129,7 @@ abstract class Option implements Monad, Matchable { abstract isNone(): this is None; static ofNone() { - return null; + return new None(); } } From a41d200d3bffc7f81309d4638573a037d74e8a48 Mon Sep 17 00:00:00 2001 From: Aitor Santana Date: Sun, 27 Oct 2024 19:24:04 +0000 Subject: [PATCH 23/27] feat(Option): :white_check_mark: Option monad should create a None --- src/option/option.ts | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/src/option/option.ts b/src/option/option.ts index c0e1d89..e4bd7ea 100644 --- a/src/option/option.ts +++ b/src/option/option.ts @@ -26,10 +26,30 @@ abstract class Option implements Monad, Matchable { return new Some(value); } - static ofSome(value: T) { + /** + * Creates an `Option` instance from a value. + * @template T The type of the value. + * @param {Nullable} value The nullable value. + * @returns {Some} A `Some` instance of the value + * @example + * const some = Option.ofSome(5); + * some.match(console.log, () => console.log('none')); // 5 + */ + static ofSome(value: T): Some { return new Some(value); } + /** + * Creates a `None` instance. + * @returns {None} A `None` instance. + * @example + * const none = Option.ofNone(); + * none.match(console.log, () => console.log('none')); // none + */ + static ofNone(): None { + return new None(); + } + /** * Creates an `Option` instance from a `Matchable` instance. * @template T The type of the value. @@ -127,10 +147,6 @@ abstract class Option implements Monad, Matchable { * none.match(console.log, none => console.log(none.isNone())); // true */ abstract isNone(): this is None; - - static ofNone() { - return new None(); - } } /** From 777a3a1f1e0f8b1c7273821e00e75c2293861536 Mon Sep 17 00:00:00 2001 From: Aitor Santana Date: Sun, 27 Oct 2024 23:11:58 +0000 Subject: [PATCH 24/27] feat(Option): :recycle: Add NotNullable type --- src/option/option.test.ts | 1 + src/option/option.ts | 8 ++++---- src/types.ts | 2 ++ tsconfig.json | 4 +++- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/option/option.test.ts b/src/option/option.test.ts index 0be6266..2907acf 100644 --- a/src/option/option.test.ts +++ b/src/option/option.test.ts @@ -52,6 +52,7 @@ describe('Option monad', () => { it('should create a Some', () => { expect(Option.ofSome(2)).toEqual(new Some(2)); + expect(Option.ofSome(null)).toEqual(new Some(null)); }); it('should create a None', () => { diff --git a/src/option/option.ts b/src/option/option.ts index e4bd7ea..3209807 100644 --- a/src/option/option.ts +++ b/src/option/option.ts @@ -1,4 +1,4 @@ -import { Nullable } from '../types'; +import { NotNullable, Nullable } from '../types'; import { Monad } from '../monad'; import { Matchable } from '../match'; @@ -29,13 +29,13 @@ abstract class Option implements Monad, Matchable { /** * Creates an `Option` instance from a value. * @template T The type of the value. - * @param {Nullable} value The nullable value. + * @param {NotNullable} value The nullable value. * @returns {Some} A `Some` instance of the value * @example * const some = Option.ofSome(5); * some.match(console.log, () => console.log('none')); // 5 */ - static ofSome(value: T): Some { + static ofSome(value: NotNullable): Option { return new Some(value); } @@ -46,7 +46,7 @@ abstract class Option implements Monad, Matchable { * const none = Option.ofNone(); * none.match(console.log, () => console.log('none')); // none */ - static ofNone(): None { + static ofNone(): Option { return new None(); } diff --git a/src/types.ts b/src/types.ts index 164a705..8bde8f6 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1 +1,3 @@ export type Nullable = T | null | undefined; +//type NotNullable = T extends null | undefined ? never : T +export type NotNullable = Exclude; diff --git a/tsconfig.json b/tsconfig.json index e4e6cd5..231fa54 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -9,7 +9,9 @@ "moduleResolution": "node", "esModuleInterop": true, "skipLibCheck": true, - "forceConsistentCasingInFileNames": true + "forceConsistentCasingInFileNames": true, + "strict": true, + "strictNullChecks": true, }, "include": ["src"], "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"] From f9f5fa8612936698b7fa8cd23e54312a9888c57b Mon Sep 17 00:00:00 2001 From: Aitor Santana Date: Mon, 28 Oct 2024 21:13:05 +0000 Subject: [PATCH 25/27] feat(Option): :fire: Remove unnecessary test --- src/option/option.test.ts | 1 - src/types.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/option/option.test.ts b/src/option/option.test.ts index 2907acf..0be6266 100644 --- a/src/option/option.test.ts +++ b/src/option/option.test.ts @@ -52,7 +52,6 @@ describe('Option monad', () => { it('should create a Some', () => { expect(Option.ofSome(2)).toEqual(new Some(2)); - expect(Option.ofSome(null)).toEqual(new Some(null)); }); it('should create a None', () => { diff --git a/src/types.ts b/src/types.ts index 8bde8f6..41acb98 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,3 +1,2 @@ export type Nullable = T | null | undefined; -//type NotNullable = T extends null | undefined ? never : T export type NotNullable = Exclude; From 506e7f34c47668386942580247207efbe28a33ad Mon Sep 17 00:00:00 2001 From: Aitor Santana Date: Tue, 29 Oct 2024 08:01:58 +0000 Subject: [PATCH 26/27] feat(Option): :recycle: Rename options methods --- src/option/option.test.ts | 4 ++-- src/option/option.ts | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/option/option.test.ts b/src/option/option.test.ts index 0be6266..0899fda 100644 --- a/src/option/option.test.ts +++ b/src/option/option.test.ts @@ -51,10 +51,10 @@ describe('Option monad', () => { }); it('should create a Some', () => { - expect(Option.ofSome(2)).toEqual(new Some(2)); + expect(Option.some(2)).toEqual(new Some(2)); }); it('should create a None', () => { - expect(Option.ofNone()).toEqual(new None()); + expect(Option.none()).toEqual(new None()); }); }); diff --git a/src/option/option.ts b/src/option/option.ts index 3209807..d5f65fe 100644 --- a/src/option/option.ts +++ b/src/option/option.ts @@ -32,10 +32,10 @@ abstract class Option implements Monad, Matchable { * @param {NotNullable} value The nullable value. * @returns {Some} A `Some` instance of the value * @example - * const some = Option.ofSome(5); + * const some = Option.some(5); * some.match(console.log, () => console.log('none')); // 5 */ - static ofSome(value: NotNullable): Option { + static some(value: NotNullable): Option { return new Some(value); } @@ -43,10 +43,10 @@ abstract class Option implements Monad, Matchable { * Creates a `None` instance. * @returns {None} A `None` instance. * @example - * const none = Option.ofNone(); + * const none = Option.none(); * none.match(console.log, () => console.log('none')); // none */ - static ofNone(): Option { + static none(): Option { return new None(); } From cbb5cc0ea877715d104bb5264ec46316bc3ca1b4 Mon Sep 17 00:00:00 2001 From: Aitor Santana Date: Thu, 31 Oct 2024 09:15:46 +0000 Subject: [PATCH 27/27] feat(Option): :recycle: Rename NotNullabe type to Present --- src/option/option.ts | 6 +++--- src/types.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/option/option.ts b/src/option/option.ts index d5f65fe..e997286 100644 --- a/src/option/option.ts +++ b/src/option/option.ts @@ -1,4 +1,4 @@ -import { NotNullable, Nullable } from '../types'; +import { Present, Nullable } from '../types'; import { Monad } from '../monad'; import { Matchable } from '../match'; @@ -29,13 +29,13 @@ abstract class Option implements Monad, Matchable { /** * Creates an `Option` instance from a value. * @template T The type of the value. - * @param {NotNullable} value The nullable value. + * @param {Present} value The nullable value. * @returns {Some} A `Some` instance of the value * @example * const some = Option.some(5); * some.match(console.log, () => console.log('none')); // 5 */ - static some(value: NotNullable): Option { + static some(value: Present): Option { return new Some(value); } diff --git a/src/types.ts b/src/types.ts index 41acb98..31161a2 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,2 +1,2 @@ export type Nullable = T | null | undefined; -export type NotNullable = Exclude; +export type Present = Exclude;