{"id":21518693,"url":"https://github.com/halo-lab/future","last_synced_at":"2026-03-06T10:32:10.602Z","repository":{"id":65580055,"uuid":"578296716","full_name":"Halo-Lab/future","owner":"Halo-Lab","description":"✨A better Promise","archived":false,"fork":false,"pushed_at":"2023-12-22T10:14:37.000Z","size":105,"stargazers_count":3,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-09T22:06:58.703Z","etag":null,"topics":["error-reporting","future","promise","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Halo-Lab.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2022-12-14T18:07:04.000Z","updated_at":"2023-05-01T09:41:44.000Z","dependencies_parsed_at":"2023-12-22T11:57:10.239Z","dependency_job_id":null,"html_url":"https://github.com/Halo-Lab/future","commit_stats":{"total_commits":12,"total_committers":2,"mean_commits":6.0,"dds":"0.16666666666666663","last_synced_commit":"80d626d86fb968dd89211095bfa55298bf85eab7"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Halo-Lab%2Ffuture","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Halo-Lab%2Ffuture/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Halo-Lab%2Ffuture/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Halo-Lab%2Ffuture/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Halo-Lab","download_url":"https://codeload.github.com/Halo-Lab/future/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248119296,"owners_count":21050755,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["error-reporting","future","promise","typescript"],"created_at":"2024-11-24T00:53:35.944Z","updated_at":"2026-03-06T10:32:10.548Z","avatar_url":"https://github.com/Halo-Lab.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Future\n\nIt is a `Promise` compatible type that allows to define and track error types.\n\n## Why is the default Promise definition type bad?\n\nAn asynchronous code may throw errors but the standard type of the `Promise` cannot tell you which errors you can handle in the `catch` method. Of course, you can define the error type explicitly, but you should know what en error can be at the time. It may be a hard task, especially if you are chaining a lot of promises and each of them may throw an error.\n\n## Installation\n\n```bash\nnpm i @halo-lab/future\n```\n\n## API\n\n1. [Overview](#usage)\n2. [Types](#types)\n3. [`of`/`Future.of`](#offutureof)\n4. [`fail`/`Future.fail`](#failfuturefail)\n5. [`from`/`Future.from`](#fromfuturefrom)\n6. [`spawn`/`Future.spawn`](#spawnfuturespawn)\n7. [`isThenable`/`Future.is`](#isthenablefutureis)\n8. [`merge`/`Future.merge`](#mergefuturemerge)\n9. [`settle`/`Future.settle`](#settlefuturesettle)\n10. [`first`/`Future.first`](#firstfuturefirst)\n11. [`oneOf`/`Future.oneOf`](#oneoffutureoneof)\n12. [`map`/`Future.map`](#mapfuturemap)\n13. [`mapErr`/`Future.mapErr`](#maperrfuturemaperr)\n14. [`recover`/`Future.recover`](#recoverfuturerecover)\n15. [`after`/`Future.after`](#afterfutureafter)\n16. [`apply`/`Future.apply`](#applyfutureapply)\n\n## Usage\n\nThis package defines `Future`/`FutureLike` types which you can use instead of the `Promise`/`PromiseLike`. These types are interchangeable.\n\n```typescript\nimport { Future } from \"@halo-lab/future\";\n\nconst future: Future\u003cnumber, Error\u003e = new Promise((resolve, reject) =\u003e {\n  const randomNumber = Math.random();\n\n  if (randomNumber \u003e 0.5) resolve(randomNumber);\n  else reject(new Error(\"Random number is less than 0.5\"));\n});\n\nconst promise: Promise\u003cnumber\u003e = future;\n```\n\nBy using the `Future` you can describe what errors a promise can be rejected with and TypeScript will help you remember and exhaustively handle them later.\n\n```typescript\n// using the example above\nconst newFuture: Future\u003cstring[], never\u003e = future.then(\n  (number) =\u003e {\n    /* do something useful */\n    return [\"foo\"]; /* some result */\n  },\n  (error /* Error */) =\u003e {\n    /* report that there is a problem and fix it */\n    return [];\n  },\n);\n```\n\n\u003e Unfortunately, `await`ed future inside the `try/catch` block cannot populate an error type to the `catch` block, because TypeScript doesn't allow it (even explicitly). Though you can refer to the future type inside the `try` block and easily get what errors are expected to be thrown.\n\u003e\n\u003e ```typescript\n\u003e try {\n\u003e   const value: string[] = await newFuture;\n\u003e } catch (error) {\n\u003e   /* error is not typed as never but any or unknown depending on your tsconfig */\n\u003e }\n\u003e ```\n\nThis package defines and exports some functions that make `Future` creation and managing easier because default `Promis`e typings are plain and don't pay any attention to the error types. These functions are exported separately and in a _namespace_ (as a default export) for convenience.\n\n### Types\n\nThe `Future` namespace defines also aliases for the `Future` type: `Self` and for the `FutureLike` type: `Like`.\n\n```typescript\nimport Future from \"@halo-lab/future\";\n\nfunction one(): Future.Self\u003c1, never\u003e {\n  return Future.of(1);\n}\n\nconst numberOne: Future.Like\u003c1, never\u003e = one();\n```\n\nBesides these types the library exports:\n\n1. `NonThenable`/`Future.Not` - a type that extracts _thenable_ types from the type argument.\n\n```typescript\ntype A = Future.Not\u003cnumber\u003e; // -\u003e number\ntype B = Future.Not\u003cPromiseLike\u003cstring\u003e\u003e; // -\u003e never\n```\n\n2. `AwaitedError`/`Future.Left` - extracts an error type from the `FutureLike`. If a type parameter isn't _thenable_, it returns `never`.\n\n```typescript\ntype A = Future.Left\u003cnumber\u003e; // -\u003e never\ntype B = Future.Left\u003cPromiseLike\u003cstring\u003e\u003e; // -\u003e unknown\ntype C = Future.Left\u003cFuture.Like\u003cstring, number\u003e\u003e; // -\u003e number\n```\n\n3. `Future.Right` - an alias to the native `Awaited` type.\n\n```typescript\ntype A = Future.Right\u003cnumber\u003e; // -\u003e number\ntype B = Future.Right\u003cPromiseLike\u003cstring\u003e\u003e; // -\u003e string\ntype C = Future.Right\u003cFuture.Like\u003cstring, number\u003e\u003e; // -\u003e string\n```\n\n### `of`/`Future.of`\n\nWraps a value with a `Future` and immediately resolves it. If the value is another `Future`, the latter isn't wrapped.\n\n```typescript\nconst wrappedNumber: Future.Self\u003c10, never\u003e = Future.of(10);\n\nconst duplicatedWrappedNumber: Future.Self\u003c10, never\u003e =\n  Future.of(wrappedNumber);\n\n// The Future created from the Promise always has an `unknown`\n// error type because it is really unknown unless the user knows it\n// and provides the type manually.\nconst fromPromise: Future.Self\u003cstring, unknown\u003e = Future.of(\n  Promise.resolve(\"foo\"),\n);\n\n// If the value is rejected Future or Promise, the resulting Future\n// also has the rejected state.\nconst failedFuture: Future\u003cnever, string\u003e = Future.of(\n  Promise.reject(\"A very helpful message\"),\n);\n```\n\n### `fail`/`Future.fail`\n\nWraps a value with a `Future` and immediately rejects it. If the value is another `Future`, it will be awaited and a new `Future` will be rejected with either value.\n\n```typescript\nconst failedFuture: Future.Self\u003cnever, \"error\"\u003e = Future.fail(\"error\");\n\nconst failedPromise: Future.Self\u003cnever, number\u003e = Future.fail(\n  Promise.resolve(7),\n);\n```\n\n### `from`/`Future.from`\n\nCreates a `Future` with an _executor_ callback. The same as the `Promise` constructor.\n\n```typescript\nconst future: Future\u003cnumber, string\u003e = Future.from((ok, err) =\u003e {\n  doAsyncJob((error, result) =\u003e (error ? err(error) : ok(result)));\n});\n```\n\n### `spawn`/`Future.spawn`\n\nCreates a `Future` from a _callback's_ result. If the callback throws an error, the `Future` will be rejected. If the callback returns another `Future` it will be returned as is.\n\n```typescript\nfunction calculateFibonacciNumber(position: number): number {\n  // ...\n}\n\nconst future: Future.Self\u003cnumber, never\u003e = Future.spawn(() =\u003e {\n  return calculateFibonacciNumber(57);\n});\n\n// There is no way to mark a function in TypeScript that can\n// throw an error, so you have to describe the error type that\n// manually. Otherwise, it will be `never`.\nconst trickyFuture: Future.Self\u003cnever, Error\u003e = Future.spawn(() =\u003e {\n  throw new Error(\"an error is thrown\");\n});\n```\n\nYou can pass arguments into the callback by providing them after it.\n\n```typescript\nconst future: Future.Self\u003cnumber, never\u003e = Future.spawn(\n  (first, second) =\u003e {\n    return first + second;\n  },\n  [34, 97],\n);\n```\n\n### `isThenable`/`Future.is`\n\nChecks if a value is a [thenable](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise#thenables) object.\n\n```typescript\nFuture.is(Future.of(1)); // -\u003e true\nFuture.is(Promise.resolve(\"foo\")); // -\u003e true\nFuture.is({\n  then(fulfill) {\n    return Future.of(fulfill(Math.random()));\n  },\n}); // -\u003e true\nFuture.is(3); // -\u003e false\n```\n\n### `merge`/`Future.merge`\n\nCombines multiple `Future`s together waiting for all to complete or first to reject. Behaves as the `Promise.all`. Accepts a variable number of arguments or a single argument that should be `Iterable` or `ArrayLike`.\n\n```typescript\nconst result: Future.Self\u003creadonly [number, string], boolean | string\u003e =\n  Future.merge(\n    Future.spawn\u003cnumber, boolean\u003e(() =\u003e mayThrowABoolean()),\n    Future.spawn\u003cstring, string\u003e(() =\u003e mayThrowAString()),\n  );\n\nconst combined: Future.Self\u003creadonly [1, 2], never\u003e = Future.merge([\n  Future.of(1),\n  Future.of(2),\n]);\n```\n\n### `settle`/`Future.settle`\n\nCombines multiple `Future`s together waiting for all to complete. Behaves as the `Promise.allSettled`. Accepts a variable number of arguments or a single argument that should be `Iterable` or `ArrayLike`. Promise's values are wrapped with the special `Result` object. It is a plain object with either `ok` property or `err`.\n\n```typescript\nconst future: Future.Self\u003c\n  readonly [Result\u003c1, never\u003e, Result\u003cnever, \"bar\"\u003e],\n  never\n\u003e = Future.settle(Future.ok(1), Future.fail(\"bar\"));\n```\n\n### `first`/`Future.first`\n\nWaits for the first `Future` to fulfill either successfuly or as a failure. Behaves as the `Promise.race`. Accepts a variable number of arguments or a single argument that should be `Iterable` or `ArrayLike`.\n\n```typescript\nconst future: Future.Self\u003c1 | \"foo\", string | boolean\u003e = Future.first(\n  Future.from\u003c1, string\u003e(\n    (ok, err) =\u003e\n      setTimeout(() =\u003e {\n        Math.random() \u003e 0.5\n          ? ok(1)\n          : err(\"numbers greater than 0.5 are not acceptable\");\n      }),\n    100,\n  ),\n  Future.spawn\u003c\"foo\", boolean\u003e(() =\u003e {\n    if (Math.random() \u003e 0.5) return \"foo\";\n    else throw true;\n  }),\n);\n```\n\n### `oneOf`/`Future.oneOf`\n\nWaits for the first `Future` to fulfill or all `Future`s to reject (array of errors is returned). Behaves as the `Promise.any`. Accepts a variable number of arguments or a single argument that should be `Iterable` or `ArrayLike`.\n\n```typescript\nconst future: Future.Self\u003c1 | \"foo\", readonly [string, boolean]\u003e = Future.oneOf(\n  Future.from\u003c1, string\u003e(\n    (ok, err) =\u003e\n      setTimeout(() =\u003e {\n        Math.random() \u003e 0.5\n          ? ok(1)\n          : err(\"numbers greater than 0.5 are not acceptable\");\n      }),\n    100,\n  ),\n  Future.spawn\u003c\"foo\", boolean\u003e(() =\u003e {\n    if (Math.random() \u003e 0.5) return \"foo\";\n    else throw true;\n  }),\n);\n```\n\n### `map`/`Future.map`\n\nTransforms a resolved value of the `Future` and returns another `Future`. It's a functional way to call _onfulfilled_ callback of `then` method. The function has curried and uncurried forms.\n\n```typescript\nconst future: Future.Self\u003c1, never\u003e = Future.of(1);\n\nconst anotherFuture: Future.Self\u003cnumber, never\u003e = Future.map(\n  future,\n  (num) =\u003e num + 1,\n);\n\nconst multiplyByTen: \u003cA\u003e(\n  future: Future.Like\u003cnumber, A\u003e,\n) =\u003e Future.Self\u003cnumber, A\u003e = Future.map((num) =\u003e num * 10);\n\nconst multipliedFuture: Future.Self\u003cnumber, never\u003e = multiplyByTen(future);\n```\n\n\u003e Callback is called only if the future is resolved. Otherwise it is returned as is.\n\n### `mapErr`/`Future.mapErr`\n\nTransforms a rejected value of the `Future` into another rejected value and returns a rejected `Future`. The function has curried and uncurried forms.\n\n```typescript\nconst future: Future.Self\u003cnever, 1\u003e = Future.fail(1);\n\nconst anotherFuture: Future.Self\u003cnever, number\u003e = Future.mapErr(\n  future,\n  (num) =\u003e num + 1,\n);\n\nconst multiplyByTen: \u003cA\u003e(\n  future: Future.Like\u003cA, number\u003e,\n) =\u003e Future.Self\u003cA, number\u003e = Future.mapErr((num) =\u003e num * 10);\n\nconst multipliedFuture: Future.Self\u003cnever, number\u003e = multiplyByTen(future);\n```\n\n\u003e Callback is called only if the future is rejected. Otherwise it is returned as is.\n\n### `recover`/`Future.recover`\n\nTransforms a rejected value of the `Future` into a resolved value and returns another `Future`. It's a functional way to call _onrejected_ callback of the `then` method or the `catch` method. The function has curried and uncurried forms.\n\n```typescript\nconst future: Future.Self\u003cOkResponse, ErrResponse\u003e = fetch(\n  \"/api/v3/endpoint\",\n).then((response) =\u003e\n  response.ok ? response.json() : Future.fail(response.json()),\n);\n\n// 1.\nconst futureWithDefaultResponse: Future.Self\u003cOkResponse, never\u003e =\n  Future.recover(future, (errResponse) =\u003e\n    createDefaultResponseFrom(errResponse),\n  );\n// 2.\nconst repairResponse: (\n  future: Future.Like\u003cOkResponse, ErrResponse\u003e,\n) =\u003e Future.Self\u003cOkResponse, never\u003e = Future.recover((errResponse) =\u003e\n  createDefaultResponseFrom(errResponse),\n);\n\nconst repairedResponse: Future.Self\u003cOkResponse, never\u003e = repairResponse(future);\n```\n\n\u003e Callback is called only if the future is rejected. Otherwise it is returned as is.\n\n### `after`/`Future.after`\n\nRegisters a callback to be called after the `Future` fulfills either way. It's a functional way to call the `finally` method. The function has curried and uncurried forms.\n\n```typescript\nconst future: Future.Self\u003cOkResponse, ErrResponse\u003e = fetch(\n  \"/api/v3/endpoint\",\n).then((response) =\u003e\n  response.ok ? response.json() : Future.fail(response.json()),\n);\n\n// 1.\nconst sameFuture: Future.Self\u003cOkResponse, ErrResponse\u003e = Future.after(\n  future,\n  () =\u003e doSomeSideEffect(),\n);\n// 2.\nconst cleanupAfterJob: \u003cOkResponse, ErrResponse\u003e(\n  future: Future.Like\u003cOkResponse, ErrResponse\u003e,\n) =\u003e Future.Self\u003cOkResponse, ErrResponse\u003e = Future.after(() =\u003e doSomeCleanup());\n\nconst sameFutureAfterCleanup: Future.Self\u003cOkResponse, ErrResponse\u003e =\n  cleanupAfterJob(future);\n```\n\nIf a callback throws an error or returns a rejected `Future` the error is propagated into the resulting `Future`.\n\n```typescript\nconst future: Future.Self\u003cnever, string | boolean\u003e = Future.after(\n  Future.fail(\"foo\"),\n  () =\u003e Future.fail(false),\n);\n```\n\n### `apply`/`Future.apply`\n\nTransforms a resolved value of the `Future` and returns another `Future`. It's a functional way to call _onfulfilled_ callback of `then` method. The function has curried and uncurried forms. It's acts the same as the [`map`](#mapfuturemap) function with a distinction that the _callback_ parameter has to be wrapped with another `FutureLike`.\n\n```typescript\nconst future: Future.Self\u003c1, never\u003e = Future.of(1);\n\nconst anotherFuture: Future.Self\u003cnumber, never\u003e = Future.apply(\n  future,\n  Future.of((num) =\u003e num + 1),\n);\n\nconst multiplyByTen: \u003cA\u003e(\n  future: Future.Like\u003cnumber, A\u003e,\n) =\u003e Future.Self\u003cnumber, A\u003e = Future.apply(Future.of((num) =\u003e num * 10));\n\nconst multipliedFuture: Future.Self\u003cnumber, never\u003e = multiplyByTen(future);\n```\n\n\u003e Callback is called only if both futures are resolved.\n\n## Word from author\n\nHave fun ✌️\n\n\u003ca href=\"https://www.halo-lab.com/?utm_source=github\"\u003e\n  \u003cimg\n    src=\"https://dgestran.sirv.com/Images/supported-by-halolab.png\"\n    alt=\"Supported by Halo lab\"\n    height=\"60\"\n  \u003e\n\u003c/a\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhalo-lab%2Ffuture","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhalo-lab%2Ffuture","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhalo-lab%2Ffuture/lists"}