Repository: karen-irc/option-t
Pull request #1460 (2022-11-01 23:01:53)

tryCatchIntoResultAsync() should catch an error thrown before normal (sync) fn `producer` returning a value
`tryCatchIntoResultAsync()` accepts all functions returns `Promise<T>` regardless of normal (sync) function or async function.

For async function, all thrown error will be a rejected promise as
returned value.
But for normal function which is just return `Promise` object,
thrown error which is before return some values will be a simple thrown error.

This means the following code will throw an error on the current code base.

```js
// This _result_ should be expected as `Err(E)` but this code simply throw an error.
const result = tryCatchIntoResultAsync(() => {
    throw new Error();
    return Promise.resolve('bar');
})
```

So we should catch it.


Patch:
diff --git a/src/PlainResult/tryCatchAsync.ts b/src/PlainResult/tryCatchAsync.ts
index 8ec77d74..ce28f0a1 100644
--- a/src/PlainResult/tryCatchAsync.ts
+++ b/src/PlainResult/tryCatchAsync.ts
@@ -13,7 +13,13 @@ import { type Result, createOk, createErr } from './Result.js';
 export function tryCatchIntoResultAsync<T>(
     producer: AsyncProducerFn<T>
 ): Promise<Result<T, unknown>> {
-    const value = producer();
+    let value: Promise<T>;
+    try {
+        value = producer();
+    } catch (e: unknown) {
+        const err = createErr<unknown>(e);
+        return Promise.resolve(err);
+    }
 
     assertIsPromise(value, ERR_MSG_PRODUCER_MUST_RETURN_PROMISE);
 


Test patch:
diff --git a/__tests__/PlainResult/try_catch_async.test.mjs b/__tests__/PlainResult/try_catch_async.test.mjs
index 377345cb..a0735641 100644
--- a/__tests__/PlainResult/try_catch_async.test.mjs
+++ b/__tests__/PlainResult/try_catch_async.test.mjs
@@ -4,7 +4,7 @@ import { isOk, isErr } from '../../__dist/esm/PlainResult/Result.mjs';
 import { tryCatchIntoResultAsync } from '../../__dist/esm/PlainResult/tryCatchAsync.mjs';
 import { unwrapErrFromResult, unwrapOkFromResult } from '../../__dist/esm/PlainResult/unwrap.mjs';
 
-test('output=Ok(T)', async (t) => {
+test('output=Ok(T): producer is async fn', async (t) => {
     t.plan(4);
 
     const EXPECTED = Math.random();
@@ -20,7 +20,23 @@ test('output=Ok(T)', async (t) => {
     t.is(unwrapOkFromResult(actual), EXPECTED, 'should contain the expect inner value');
 });
 
-test('output=Err(unknown)', async (t) => {
+test('output=Ok(T): producer is normal fn', async (t) => {
+    t.plan(4);
+
+    const EXPECTED = Math.random();
+    const result = tryCatchIntoResultAsync(() => {
+        t.pass();
+        return Promise.resolve(EXPECTED);
+    });
+
+    t.true(result instanceof Promise, 'result should be Promise');
+
+    const actual = await result;
+    t.true(isOk(actual), 'should be Ok(T)');
+    t.is(unwrapOkFromResult(actual), EXPECTED, 'should contain the expect inner value');
+});
+
+test('output=Err(unknown): producer is async fn', async (t) => {
     t.plan(4);
 
     const EXPECTED = new Error(Math.random());
@@ -36,6 +52,39 @@ test('output=Err(unknown)', async (t) => {
     t.is(unwrapErrFromResult(actual), EXPECTED, 'should contain the expect inner value');
 });
 
+test('output=Err(unknown): producer is normal fn', async (t) => {
+    t.plan(4);
+
+    const EXPECTED = new Error(Math.random());
+    const result = tryCatchIntoResultAsync(() => {
+        t.pass();
+        return Promise.reject(EXPECTED);
+    });
+
+    t.true(result instanceof Promise, 'result should be Promise');
+
+    const actual = await result;
+    t.true(isErr(actual), 'should be Err(E)');
+    t.is(unwrapErrFromResult(actual), EXPECTED, 'should contain the expect inner value');
+});
+
+test('output=Err(unknown): producer is normal fn but throw an error before return any Promise', async (t) => {
+    t.plan(4);
+
+    const EXPECTED = new Error(Math.random());
+
+    const result = tryCatchIntoResultAsync(() => {
+        t.pass();
+        throw EXPECTED;
+    });
+
+    t.true(result instanceof Promise, 'result should be Promise');
+
+    const actual = await result;
+    t.true(isErr(actual), 'should be Err(E)');
+    t.is(unwrapErrFromResult(actual), EXPECTED, 'should contain the expect inner value');
+});
+
 test('if producer does not return Promise, case = return', async (t) => {
     t.plan(2);