typescript-functional-extensions
typescript-functional-extensions copied to clipboard
Unexpected Unit in Result-Monad after a function returns null
Describe the bug
When an asynchronous function returns null
I expect either null
to be the value or that the Result turns into a failure, but Unit
is returned.
To Reproduce Steps to reproduce the behaviour:
- Go to https://stackblitz.com/edit/typescript-tp3pbw?file=index.ts
- Check the code
- Have a look at the preview website, where
Unit
is printed.
Expected behaviour
I expect either null
to be the value or that the Result turns into a failure
Screenshots
- Function resolves
null
- Expecting Value to be
null
and checking for it inensure
- Since
Unit
is returned,.ensure
has no effect.
This issue is in ResultAsync.from
(https://github.com/seangwright/typescript-functional-extensions/blob/main/src/resultAsync.ts#L57)
If the value in the Promise
isn't a Result<T>
, then ResultAsync.from
initializes its internal Result
with Result.success
which guards against null
or undefined
values for success with an isSome(value)
check. In this specific case Result.success
can actually replace the success value with Unit
for null
or undefined
.
new Result<Unit, TError>({
value: Unit.Instance,
error: undefined,
isSuccess: true,
}
This means value
cannot be null
or undefined
or void
when using Result.success
(which ResultAsync.from
uses in this case).
Result.success
should be updated to store null
and undefined
and void
as success values.
ResultAsync.from
is also strange because it treats all values as success
, however this is stated in the comment of the method
/**
* Creates a new ResultAsync from the given Promise
* @param value a Promise which will be converted into a successful Result if it resolves
* and a failed Result if it rejects
*/
from(...)
Finally, there are checks inside Result
which prevent null
/undefined
/void
values from being stored as the success value. Namely the constructor and getValueOrThrow()
which returns a value of Some<TValue>
. If we want Result
to be typeable as Result<null, string>
then a lot of work will need to be done to allow it to succeed with null
/undefined
/void
.
@GregOnNet Coming back to this after looking at the types for #24 ... maybe the better approach is to do this:
function serviceReturningNothing: Promise<Maybe<string>> {
return Promise.resolve(Maybe.none<string>());
}