Repository: true-myth/true-myth
Pull request #916 (2025-01-10 03:49:08)

Task: add module-scoped `resolve`, `reject`, and `withResolvers`
Nothing fancy, just aliases. These, unlike some of the others, I expect we will keep on the `Task` class as statics, because it is valuable for the `Task` class to implement the static part of the `Promise` API.


Patch:
diff --git a/src/task.ts b/src/task.ts
index 5a8e478..a6c21d7 100644
--- a/src/task.ts
+++ b/src/task.ts
@@ -1346,6 +1346,15 @@ class Timeout extends Error {
 // otherwise): people ought not be subclassing or instantiating `Timeout`!
 export type { Timeout };
 
+/** Standalone function version of {@linkcode Task.resolve} */
+export const resolve = Task.resolve;
+
+/** Standalone function version of {@linkcode Task.reject} */
+export const reject = Task.reject;
+
+/** Standalone function version of {@linkcode Task.withResolvers} */
+export const withResolvers = Task.withResolvers;
+
 /**
   Produce a {@linkcode Task Task<T, unknown>} from a [`Promise`][mdn-promise].
 


Test patch:
diff --git a/test/task.test.ts b/test/task.test.ts
index 138b144..affd14c 100644
--- a/test/task.test.ts
+++ b/test/task.test.ts
@@ -23,6 +23,9 @@ import Task, {
   fromPromise,
   fromUnsafePromise,
   fromResult,
+  resolve,
+  reject,
+  withResolvers,
 } from 'true-myth/task';
 import Maybe from 'true-myth/maybe';
 import Result from 'true-myth/result';
@@ -1761,6 +1764,70 @@ describe('module-scope functions', () => {
     expect(unwrap(result)).toEqual(ms);
   });
 
+  describe('resolve', () => {
+    test('produces `Task<Unit, never>` when passed no arguments', () => {
+      let theTask = resolve();
+      expectTypeOf(theTask).toEqualTypeOf<Task<Unit, never>>();
+    });
+
+    test('produces `Task<T, never>` when passed a basic argument', () => {
+      let theValue = 'hello';
+      let theTask = resolve(theValue);
+      expectTypeOf(theTask).toEqualTypeOf<Task<string, never>>();
+    });
+
+    test('allows explicitly setting a type for `E`', () => {
+      let resolvedWithUnit = resolve<Unit, string>();
+      expectTypeOf(resolvedWithUnit).toEqualTypeOf<Task<Unit, string>>();
+
+      let resolvedWithValue = resolve<string, number>('hello');
+      expectTypeOf(resolvedWithValue).toEqualTypeOf<Task<string, number>>();
+    });
+  });
+
+  describe('reject', () => {
+    test('produces `Task<never, Unit>` when passed no arguments', () => {
+      let theTask = reject();
+      expectTypeOf(theTask).toEqualTypeOf<Task<never, Unit>>();
+    });
+
+    test('produces `Task<never, E>` when passed an argument', () => {
+      let theReason = 'uh oh';
+      let theTask = reject(theReason);
+      expectTypeOf(theTask).toEqualTypeOf<Task<never, string>>();
+    });
+
+    test('allows explicitly setting a type for `T`', () => {
+      let rejectedWithUnit = reject<string>();
+      expectTypeOf(rejectedWithUnit).toEqualTypeOf<Task<string, Unit>>();
+
+      let rejectedWithValue = reject<string, number>(123);
+      expectTypeOf(rejectedWithValue).toEqualTypeOf<Task<string, number>>();
+    });
+  });
+
+  describe('withResolvers', () => {
+    test('supports resolving', async () => {
+      let { task, resolve } = withResolvers<string, never>();
+      expectTypeOf(task).toEqualTypeOf<Task<string, never>>();
+
+      let theValue = 'hello';
+      resolve(theValue);
+      let result = await task;
+      expect(unwrap(result)).toEqual(theValue);
+    });
+
+    test('supports rejecting', async () => {
+      let { task, reject } = withResolvers<never, string>();
+      expectTypeOf(task).toEqualTypeOf<Task<never, string>>();
+
+      let theReason = 'le sigh';
+      reject(theReason);
+      let result = await task;
+      expect(unwrapErr(result)).toEqual(theReason);
+    });
+  });
+
   describe('safelyTry', () => {
     describe('with a non-throwing function', () => {
       test('with a promise that resolves', async () => {