diff --git a/404.html b/404.html index b476134..458ab50 100644 --- a/404.html +++ b/404.html @@ -5,13 +5,13 @@ Page Not Found | Boxed - +
Skip to main content

Page Not Found

We could not find what you were looking for.

Please contact the owner of the site that linked you to the original URL and let them know their link is broken.

- + \ No newline at end of file diff --git a/array/index.html b/array/index.html index 3fe7dfa..9220831 100644 --- a/array/index.html +++ b/array/index.html @@ -5,13 +5,13 @@ Array | Boxed - +
Skip to main content

Array

import { Array } from "@swan-io/boxed";

Array.filterMap(array, func)

Returns an array containing the Option.Some values returned by func for each array item.

This function can be useful to refine the types of an array.

Examples
Array.filterMap([1, 2, 3], (x) =>
isEven(x) === 0 ? Option.Some(x) : Option.None(),
); // [2]

Array.findMap(array, func)

Returns the first Option.Some value returned by func for each array item.

Examples
Array.findMap([1, 2, 3], (x) =>
isEven(x) === 0 ? Option.Some(x) : Option.None(),
); // Option.Some(2)

Array.find(array, predicate)

Return the first item in the array for which predicate returns true.

The function returns an Option so that we can distinguish between a found nullish value and a not found value.

Examples
Array.find(array, (x) => x === undefined);
// Some(undefined) if found
// None if not found

Array.findIndex(array, predicate)

Return the first index in the array for which predicate returns true.

The function returns an Option.

Examples
Array.findIndex(array, (x) => x === undefined);
// Some(index) if found
// None if not found

Array.binarySearchBy(sortedArray, item, compare)

Performs a binary search on the array.

Returns the index of the item if there's an exact match, return the index of the first superior value if not. Return -1 if the array is empty.

Examples
const index = Array.binarySearchBy(array, "my value");

Array.zip(arrayA, arrayB)

Create an array of pairs from two arrays.

Examples
Array.zip([1, 2, 3], ["one", "two", "three"]);
// [[1, "one"], [2, "two"], [3, "three"]]

Array.unzip(arrayOfPairs)

Turns an array of pairs into two arrays.

Examples
Array.unzip([
[1, "one"],
[2, "two"],
[3, "three"],
]);
// [[1, 2, 3], ["one", "two", "three"]]

Array.from(arrayLike)

Array.from, reexported for convenience when Boxed Array shadows the Array constructor in scope.

Examples
Array.from({ length: 3 }, (_, key) => key); // [0, 1, 2]

Array.of(...items)

Array.of, reexported for convenience when Boxed Array shadows the Array constructor in scope.

Examples
Array.of(1, 2, 3); // [1, 2, 3]

Array.isArray(value)

Array.isArray, reexported for convenience when Boxed Array shadows the Array constructor in scope.

Examples
Array.isArray("");
// false

Array.isArray([1, 2, 3]);
// true
- + \ No newline at end of file diff --git a/assets/js/618f8fb4.66c10766.js b/assets/js/618f8fb4.66c10766.js new file mode 100644 index 0000000..5ca2e13 --- /dev/null +++ b/assets/js/618f8fb4.66c10766.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[476],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>N});var a=n(7294);function l(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function i(e){for(var t=1;t=0||(l[n]=e[n]);return l}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(l[n]=e[n])}return l}var r=a.createContext({}),m=function(e){var t=a.useContext(r),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},u=function(e){var t=m(e.components);return a.createElement(r.Provider,{value:t},e.children)},s="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},k=a.forwardRef((function(e,t){var n=e.components,l=e.mdxType,o=e.originalType,r=e.parentName,u=p(e,["components","mdxType","originalType","parentName"]),s=m(n),k=l,N=s["".concat(r,".").concat(k)]||s[k]||d[k]||o;return n?a.createElement(N,i(i({ref:t},u),{},{components:n})):a.createElement(N,i({ref:t},u))}));function N(e,t){var n=arguments,l=t&&t.mdxType;if("string"==typeof e||l){var o=n.length,i=new Array(o);i[0]=k;var p={};for(var r in t)hasOwnProperty.call(t,r)&&(p[r]=t[r]);p.originalType=e,p[s]="string"==typeof e?e:l,i[1]=p;for(var m=2;m{n.r(t),n.d(t,{assets:()=>r,contentTitle:()=>i,default:()=>d,frontMatter:()=>o,metadata:()=>p,toc:()=>m});var a=n(7462),l=(n(7294),n(3905));const o={title:"Option",sidebar_label:"Option"},i=void 0,p={unversionedId:"option",id:"option",title:"Option",description:"The Option type can be used as a replacement for null and undefined when manipulating optional data. Contrary to null and undefined, an option is kind of like a box, that contains a value or not.",source:"@site/docs/option.md",sourceDirName:".",slug:"/option",permalink:"/boxed/option",draft:!1,editUrl:"https://github.com/swan-io/boxed/edit/main/docs/docs/option.md",tags:[],version:"current",frontMatter:{title:"Option",sidebar_label:"Option"},sidebar:"docs",previous:{title:"Design choices",permalink:"/boxed/design-choices"},next:{title:"Result",permalink:"/boxed/result"}},r={},m=[{value:"Create an Option value",id:"create-an-option-value",level:2},{value:"Methods",id:"methods",level:2},{value:".map(f)",id:"mapf",level:3},{value:".flatMap(f)",id:"flatmapf",level:3},{value:".filter(f)",id:"filterf",level:3},{value:".getOr(defaultValue)",id:"getordefaultvalue",level:3},{value:".get()",id:"get",level:3},{value:".isSome()",id:"issome",level:3},{value:".isNone()",id:"isnone",level:3},{value:".toNull()",id:"tonull",level:3},{value:".toUndefined()",id:"toundefined",level:3},{value:".toResult(errorWhenNone)",id:"toresulterrorwhennone",level:3},{value:".match()",id:"match",level:3},{value:".tap(func)",id:"tapfunc",level:3},{value:".tapSome(func)",id:"tapsomefunc",level:3},{value:"Statics",id:"statics",level:2},{value:"Option.isOption(value)",id:"optionisoptionvalue",level:3},{value:"Option.all(options)",id:"optionalloptions",level:3},{value:"Option.allFromDict(options)",id:"optionallfromdictoptions",level:3},{value:"TS Pattern interop",id:"ts-pattern-interop",level:2},{value:"Cheatsheet",id:"cheatsheet",level:2}],u={toc:m},s="wrapper";function d(e){let{components:t,...n}=e;return(0,l.kt)(s,(0,a.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,l.kt)("p",null,"The ",(0,l.kt)("inlineCode",{parentName:"p"},"Option")," type can be used as a replacement for ",(0,l.kt)("inlineCode",{parentName:"p"},"null")," and ",(0,l.kt)("inlineCode",{parentName:"p"},"undefined")," when manipulating optional data. Contrary to ",(0,l.kt)("inlineCode",{parentName:"p"},"null")," and ",(0,l.kt)("inlineCode",{parentName:"p"},"undefined"),", an option is kind of like a box, that contains a value or not."),(0,l.kt)("p",null,"It can be useful to distinguish values between each other: you can represent ",(0,l.kt)("inlineCode",{parentName:"p"},"Some(None)")," with options, whereas ",(0,l.kt)("inlineCode",{parentName:"p"},"undefined")," or ",(0,l.kt)("inlineCode",{parentName:"p"},"null")," replace the value they intend to make optional."),(0,l.kt)("p",null,"An option can have two possible states:"),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("inlineCode",{parentName:"li"},"Some(value)")),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("inlineCode",{parentName:"li"},"None"))),(0,l.kt)("h2",{id:"create-an-option-value"},"Create an Option value"),(0,l.kt)("p",null,"To create an option, use the ",(0,l.kt)("inlineCode",{parentName:"p"},"Some")," and ",(0,l.kt)("inlineCode",{parentName:"p"},"None")," constructors:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},'import { Option } from "@swan-io/boxed";\n\nconst aName = Option.Some("John");\nconst bName = Option.None();\n\n// You can enforce the type using a type parameter\nOption.Some("John");\nOption.None();\n')),(0,l.kt)("p",null,"You get interop with ",(0,l.kt)("inlineCode",{parentName:"p"},"null")," and ",(0,l.kt)("inlineCode",{parentName:"p"},"undefined"),":"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"// `value` being `null` or `undefined` makes a `None`\nconst a = Option.fromNullable(value);\n\n// `value` being `null` makes a `None`\nconst b = Option.fromNull(value);\n\n// `value` being `undefined` makes a `None`\nconst c = Option.fromUndefined(value);\n")),(0,l.kt)("admonition",{title:"Since v3.0.0",type:"tip"},(0,l.kt)("p",{parentName:"admonition"},(0,l.kt)("inlineCode",{parentName:"p"},"Option")," values are referentially equal if they contain the same value, meaning that ",(0,l.kt)("inlineCode",{parentName:"p"},"Option.Some(1) === Option.Some(1)"),".")),(0,l.kt)("h2",{id:"methods"},"Methods"),(0,l.kt)("p",null,"The option type provides a few manipulation functions:"),(0,l.kt)("h3",{id:"mapf"},".map(f)"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"Option.map(f: (value: A) => B): Option\n")),(0,l.kt)("p",null,"If the option is ",(0,l.kt)("inlineCode",{parentName:"p"},"Some(value)")," returns ",(0,l.kt)("inlineCode",{parentName:"p"},"Some(f(value))"),", otherwise returns ",(0,l.kt)("inlineCode",{parentName:"p"},"None"),"."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"Option.Some(2).map((x) => x * 2);\n// Option.Some<4>\n\nOption.None().map((x) => x * 2);\n// Option.None\n")),(0,l.kt)("h3",{id:"flatmapf"},".flatMap(f)"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"Option.flatMap(f: (value: A) => Option): Option\n")),(0,l.kt)("p",null,"If the option is ",(0,l.kt)("inlineCode",{parentName:"p"},"Some(value)")," returns ",(0,l.kt)("inlineCode",{parentName:"p"},"f(value)"),", otherwise returns ",(0,l.kt)("inlineCode",{parentName:"p"},"None"),"."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"Option.Some(3).flatMap((x) => (x > 2 ? Option.None() : Option.Some(2)));\n// Option.None\n\nOption.Some(1).flatMap((x) => (x > 2 ? Option.None() : Option.Some(2)));\n// Option.Some<2>\n\noption.flatMap((value) => value.optionalProperty);\n// Option\n")),(0,l.kt)("h3",{id:"filterf"},".filter(f)"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"Option.filter(f: (value: A) => boolean): Option\n")),(0,l.kt)("p",null,"If the option is ",(0,l.kt)("inlineCode",{parentName:"p"},"Some(value)")," and that ",(0,l.kt)("inlineCode",{parentName:"p"},"f(value)")," is ",(0,l.kt)("inlineCode",{parentName:"p"},"true"),", returns ",(0,l.kt)("inlineCode",{parentName:"p"},"Some(value)"),", otherwise returns ",(0,l.kt)("inlineCode",{parentName:"p"},"None"),"."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"Option.Some(3).filter((x) => x > 2);\n// Option.Some(3)\n\nOption.Some(1).filter((x) => x > 2);\n// Option.None\n")),(0,l.kt)("h3",{id:"getordefaultvalue"},".getOr(defaultValue)"),(0,l.kt)("blockquote",null,(0,l.kt)("p",{parentName:"blockquote"},"Alias: ",(0,l.kt)("inlineCode",{parentName:"p"},"getWithDefault"))),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"Option.getOr(defaultValue: A): A\n")),(0,l.kt)("p",null,"If the option is ",(0,l.kt)("inlineCode",{parentName:"p"},"Some(value)")," returns ",(0,l.kt)("inlineCode",{parentName:"p"},"value"),", otherwise returns ",(0,l.kt)("inlineCode",{parentName:"p"},"defaultValue"),"."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"Option.Some(2).getOr(1);\n// 2\n\nOption.None().getOr(1);\n// 1\n")),(0,l.kt)("h3",{id:"get"},".get()"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"Option.get(): A\n")),(0,l.kt)("p",null,"Returns the value contained in ",(0,l.kt)("inlineCode",{parentName:"p"},"Some(value)"),". Only usable within a ",(0,l.kt)("inlineCode",{parentName:"p"},"isSome()")," check."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"const value = option.get();\n// does not compile\n\nif (option.isSome()) {\n const value = option.get();\n // value\n}\n")),(0,l.kt)("h3",{id:"issome"},".isSome()"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"Option.isSome(): boolean\n")),(0,l.kt)("p",null,"Type guard. Checks if the option is ",(0,l.kt)("inlineCode",{parentName:"p"},"Some(value)")),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"Option.Some(2).isSome();\n// true\n\nOption.None().isSome();\n// false\n\nif (option.isSome()) {\n const value = option.get();\n}\n")),(0,l.kt)("h3",{id:"isnone"},".isNone()"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"Option.isNone(): boolean\n")),(0,l.kt)("p",null,"Type guard. Checks if the option is ",(0,l.kt)("inlineCode",{parentName:"p"},"None")),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"Option.Some(2).isNone();\n// false\n\nOption.None().isNone();\n// true\n")),(0,l.kt)("h3",{id:"tonull"},".toNull()"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"Option.toNull(): A | null\n")),(0,l.kt)("p",null,"Returns ",(0,l.kt)("inlineCode",{parentName:"p"},"null")," if the option is ",(0,l.kt)("inlineCode",{parentName:"p"},"None"),", returns the value otherwise"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"Option.Some(2).toNull();\n// 2\n\nOption.None().toNull();\n// null\n")),(0,l.kt)("h3",{id:"toundefined"},".toUndefined()"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"Option.toUndefined(): A | undefined\n")),(0,l.kt)("p",null,"Returns ",(0,l.kt)("inlineCode",{parentName:"p"},"undefined")," if the option is ",(0,l.kt)("inlineCode",{parentName:"p"},"None"),", returns the value otherwise"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"Option.Some(2).toUndefined();\n// 2\n\nOption.None().toUndefined();\n// undefined\n")),(0,l.kt)("h3",{id:"toresulterrorwhennone"},".toResult(errorWhenNone)"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"Option.toResult(valueWhenNone: E): Result\n")),(0,l.kt)("p",null,"Returns ",(0,l.kt)("inlineCode",{parentName:"p"},"Ok")," if the option is ",(0,l.kt)("inlineCode",{parentName:"p"},"Some"),", returns ",(0,l.kt)("inlineCode",{parentName:"p"},"Error")," otherwise"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},'const a = Option.Some(1).toResult("NotFound");\n// Ok<1>\n\nconst b = Option.None().toResult("NotFound");\n// Error<"NotFound">\n')),(0,l.kt)("h3",{id:"match"},".match()"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"Option.match(config: {\n Some: (value: A) => B;\n None: () => B;\n}): B\n")),(0,l.kt)("p",null,"Match the option state"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},'const valueToDisplay = option.match({\n Some: (value) => value,\n None: () => "No value",\n});\n// value | "No value"\n')),(0,l.kt)("h3",{id:"tapfunc"},".tap(func)"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"Option.tap(func: (option: Option) => unknown): Option\n")),(0,l.kt)("p",null,"Executes ",(0,l.kt)("inlineCode",{parentName:"p"},"func")," with ",(0,l.kt)("inlineCode",{parentName:"p"},"option"),", and returns ",(0,l.kt)("inlineCode",{parentName:"p"},"option"),". Useful for logging and debugging."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"option.tap(console.log).map((x) => x * 2);\n")),(0,l.kt)("h3",{id:"tapsomefunc"},".tapSome(func)"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"Option.tapSome(func: (option: A) => unknown): Option\n")),(0,l.kt)("p",null,"Executes ",(0,l.kt)("inlineCode",{parentName:"p"},"func")," with ",(0,l.kt)("inlineCode",{parentName:"p"},"option"),"'s value if ",(0,l.kt)("inlineCode",{parentName:"p"},"Some"),", and returns ",(0,l.kt)("inlineCode",{parentName:"p"},"option"),". Useful for logging and debugging."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"option.tapSome(console.log).map((x) => x * 2);\n")),(0,l.kt)("h2",{id:"statics"},"Statics"),(0,l.kt)("h3",{id:"optionisoptionvalue"},"Option.isOption(value)"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"isOption(value: unknown): boolean\n")),(0,l.kt)("p",null,"Type guard, checks if the provided value is an option."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"Option.isOption(Option.Some(1));\n// true\n\nOption.isOption([]);\n// false\n")),(0,l.kt)("h3",{id:"optionalloptions"},"Option.all(options)"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"all(options: Array>): Option>\n")),(0,l.kt)("p",null,'Turns an "array of options of value" into a "option of array of value".'),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"Option.all([Option.Some(1), Option.Some(2), Option.Some(3)]);\n// Some([1, 2, 3])\n\nOption.all([Option.None(), Option.Some(2), Option.Some(3)]);\n// None\n")),(0,l.kt)("h3",{id:"optionallfromdictoptions"},"Option.allFromDict(options)"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"allFromDict(options: Dict>): Option>\n")),(0,l.kt)("p",null,'Turns a "dict of options of value" into a "option of dict of value".'),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"Option.allFromDict({ a: Option.Some(1), b: Option.Some(2), c: Option.Some(3) });\n// Some({a: 1, b: 2, c: 3})\n\nOption.allFromDict({ a: Option.None(), b: Option.Some(2), c: Option.Some(3) });\n// None\n")),(0,l.kt)("h2",{id:"ts-pattern-interop"},"TS Pattern interop"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},'import { match, P } from "ts-pattern";\nimport { Option } from "@swan-io/boxed";\n\nmatch(myOption)\n .with(Option.P.Some(P.select()), (value) => console.log(value))\n .with(Option.P.None, () => "No value")\n .exhaustive();\n')),(0,l.kt)("h2",{id:"cheatsheet"},"Cheatsheet"),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Method"),(0,l.kt)("th",{parentName:"tr",align:null},"Input"),(0,l.kt)("th",{parentName:"tr",align:null},"Function input"),(0,l.kt)("th",{parentName:"tr",align:null},"Function output"),(0,l.kt)("th",{parentName:"tr",align:null},"Returned value"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"#mapf"},(0,l.kt)("inlineCode",{parentName:"a"},"map"))),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"Some(x)")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"x")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"y")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"Some(y)"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"#mapf"},(0,l.kt)("inlineCode",{parentName:"a"},"map"))),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"None()")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("em",{parentName:"td"},"not provided")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("em",{parentName:"td"},"not executed")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"None()"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"#flatmapf"},(0,l.kt)("inlineCode",{parentName:"a"},"flatMap"))),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"Some(x)")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"x")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"Some(y)")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"Some(y)"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"#flatmapf"},(0,l.kt)("inlineCode",{parentName:"a"},"flatMap"))),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"Some(x)")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"x")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"None()")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"None()"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"#flatmapf"},(0,l.kt)("inlineCode",{parentName:"a"},"flatMap"))),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"None()")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("em",{parentName:"td"},"not provided")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("em",{parentName:"td"},"not executed")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"None()"))))))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/618f8fb4.80c6c655.js b/assets/js/618f8fb4.80c6c655.js deleted file mode 100644 index 2133aff..0000000 --- a/assets/js/618f8fb4.80c6c655.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[476],{3905:(e,t,n)=>{n.d(t,{Zo:()=>u,kt:()=>N});var a=n(7294);function l(e,t,n){return t in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}function o(e,t){var n=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),n.push.apply(n,a)}return n}function i(e){for(var t=1;t=0||(l[n]=e[n]);return l}(e,t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(e);for(a=0;a=0||Object.prototype.propertyIsEnumerable.call(e,n)&&(l[n]=e[n])}return l}var r=a.createContext({}),m=function(e){var t=a.useContext(r),n=t;return e&&(n="function"==typeof e?e(t):i(i({},t),e)),n},u=function(e){var t=m(e.components);return a.createElement(r.Provider,{value:t},e.children)},s="mdxType",d={inlineCode:"code",wrapper:function(e){var t=e.children;return a.createElement(a.Fragment,{},t)}},k=a.forwardRef((function(e,t){var n=e.components,l=e.mdxType,o=e.originalType,r=e.parentName,u=p(e,["components","mdxType","originalType","parentName"]),s=m(n),k=l,N=s["".concat(r,".").concat(k)]||s[k]||d[k]||o;return n?a.createElement(N,i(i({ref:t},u),{},{components:n})):a.createElement(N,i({ref:t},u))}));function N(e,t){var n=arguments,l=t&&t.mdxType;if("string"==typeof e||l){var o=n.length,i=new Array(o);i[0]=k;var p={};for(var r in t)hasOwnProperty.call(t,r)&&(p[r]=t[r]);p.originalType=e,p[s]="string"==typeof e?e:l,i[1]=p;for(var m=2;m{n.r(t),n.d(t,{assets:()=>r,contentTitle:()=>i,default:()=>d,frontMatter:()=>o,metadata:()=>p,toc:()=>m});var a=n(7462),l=(n(7294),n(3905));const o={title:"Option",sidebar_label:"Option"},i=void 0,p={unversionedId:"option",id:"option",title:"Option",description:"The Option type can be used as a replacement for null and undefined when manipulating optional data. Contrary to null and undefined, an option is kind of like a box, that contains a value or not.",source:"@site/docs/option.md",sourceDirName:".",slug:"/option",permalink:"/boxed/option",draft:!1,editUrl:"https://github.com/swan-io/boxed/edit/main/docs/docs/option.md",tags:[],version:"current",frontMatter:{title:"Option",sidebar_label:"Option"},sidebar:"docs",previous:{title:"Design choices",permalink:"/boxed/design-choices"},next:{title:"Result",permalink:"/boxed/result"}},r={},m=[{value:"Create an Option value",id:"create-an-option-value",level:2},{value:"Methods",id:"methods",level:2},{value:".map(f)",id:"mapf",level:3},{value:".flatMap(f)",id:"flatmapf",level:3},{value:".filter(f)",id:"filterf",level:3},{value:".getOr(defaultValue)",id:"getordefaultvalue",level:3},{value:".get()",id:"get",level:3},{value:".isSome()",id:"issome",level:3},{value:".isNone()",id:"isnone",level:3},{value:".toNull()",id:"tonull",level:3},{value:".toUndefined()",id:"toundefined",level:3},{value:".toResult(errorWhenNone)",id:"toresulterrorwhennone",level:3},{value:".match()",id:"match",level:3},{value:".tap(func)",id:"tapfunc",level:3},{value:".tapSome(func)",id:"tapsomefunc",level:3},{value:"Statics",id:"statics",level:2},{value:"Option.isOption(value)",id:"optionisoptionvalue",level:3},{value:"Option.all(options)",id:"optionalloptions",level:3},{value:"Option.allFromDict(options)",id:"optionallfromdictoptions",level:3},{value:"TS Pattern interop",id:"ts-pattern-interop",level:2},{value:"Cheatsheet",id:"cheatsheet",level:2}],u={toc:m},s="wrapper";function d(e){let{components:t,...n}=e;return(0,l.kt)(s,(0,a.Z)({},u,n,{components:t,mdxType:"MDXLayout"}),(0,l.kt)("p",null,"The ",(0,l.kt)("inlineCode",{parentName:"p"},"Option")," type can be used as a replacement for ",(0,l.kt)("inlineCode",{parentName:"p"},"null")," and ",(0,l.kt)("inlineCode",{parentName:"p"},"undefined")," when manipulating optional data. Contrary to ",(0,l.kt)("inlineCode",{parentName:"p"},"null")," and ",(0,l.kt)("inlineCode",{parentName:"p"},"undefined"),", an option is kind of like a box, that contains a value or not."),(0,l.kt)("p",null,"It can be useful to distinguish values between each other: you can represent ",(0,l.kt)("inlineCode",{parentName:"p"},"Some(None)")," with options, whereas ",(0,l.kt)("inlineCode",{parentName:"p"},"undefined")," or ",(0,l.kt)("inlineCode",{parentName:"p"},"null")," replace the value they intend to make optional."),(0,l.kt)("p",null,"An option can have two possible states:"),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("inlineCode",{parentName:"li"},"Some(value)")),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("inlineCode",{parentName:"li"},"None"))),(0,l.kt)("h2",{id:"create-an-option-value"},"Create an Option value"),(0,l.kt)("p",null,"To create an option, use the ",(0,l.kt)("inlineCode",{parentName:"p"},"Some")," and ",(0,l.kt)("inlineCode",{parentName:"p"},"None")," constructors:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},'import { Option } from "@swan-io/boxed";\n\nconst aName = Option.Some("John");\nconst bName = Option.None();\n\n// You can enforce the type using a type parameter\nOption.Some("John");\nOption.None();\n')),(0,l.kt)("p",null,"You get interop with ",(0,l.kt)("inlineCode",{parentName:"p"},"null")," and ",(0,l.kt)("inlineCode",{parentName:"p"},"undefined"),":"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"// `value` being `null` or `undefined` makes a `None`\nconst a = Option.fromNullable(value);\n\n// `value` being `null` makes a `None`\nconst b = Option.fromNull(value);\n\n// `value` being `undefined` makes a `None`\nconst c = Option.fromUndefined(value);\n")),(0,l.kt)("h2",{id:"methods"},"Methods"),(0,l.kt)("p",null,"The option type provides a few manipulation functions:"),(0,l.kt)("h3",{id:"mapf"},".map(f)"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"Option.map(f: (value: A) => B): Option\n")),(0,l.kt)("p",null,"If the option is ",(0,l.kt)("inlineCode",{parentName:"p"},"Some(value)")," returns ",(0,l.kt)("inlineCode",{parentName:"p"},"Some(f(value))"),", otherwise returns ",(0,l.kt)("inlineCode",{parentName:"p"},"None"),"."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"Option.Some(2).map((x) => x * 2);\n// Option.Some<4>\n\nOption.None().map((x) => x * 2);\n// Option.None\n")),(0,l.kt)("h3",{id:"flatmapf"},".flatMap(f)"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"Option.flatMap(f: (value: A) => Option): Option\n")),(0,l.kt)("p",null,"If the option is ",(0,l.kt)("inlineCode",{parentName:"p"},"Some(value)")," returns ",(0,l.kt)("inlineCode",{parentName:"p"},"f(value)"),", otherwise returns ",(0,l.kt)("inlineCode",{parentName:"p"},"None"),"."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"Option.Some(3).flatMap((x) => (x > 2 ? Option.None() : Option.Some(2)));\n// Option.None\n\nOption.Some(1).flatMap((x) => (x > 2 ? Option.None() : Option.Some(2)));\n// Option.Some<2>\n\noption.flatMap((value) => value.optionalProperty);\n// Option\n")),(0,l.kt)("h3",{id:"filterf"},".filter(f)"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"Option.filter(f: (value: A) => boolean): Option\n")),(0,l.kt)("p",null,"If the option is ",(0,l.kt)("inlineCode",{parentName:"p"},"Some(value)")," and that ",(0,l.kt)("inlineCode",{parentName:"p"},"f(value)")," is ",(0,l.kt)("inlineCode",{parentName:"p"},"true"),", returns ",(0,l.kt)("inlineCode",{parentName:"p"},"Some(value)"),", otherwise returns ",(0,l.kt)("inlineCode",{parentName:"p"},"None"),"."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"Option.Some(3).filter((x) => x > 2);\n// Option.Some(3)\n\nOption.Some(1).filter((x) => x > 2);\n// Option.None\n")),(0,l.kt)("h3",{id:"getordefaultvalue"},".getOr(defaultValue)"),(0,l.kt)("blockquote",null,(0,l.kt)("p",{parentName:"blockquote"},"Alias: ",(0,l.kt)("inlineCode",{parentName:"p"},"getWithDefault"))),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"Option.getOr(defaultValue: A): A\n")),(0,l.kt)("p",null,"If the option is ",(0,l.kt)("inlineCode",{parentName:"p"},"Some(value)")," returns ",(0,l.kt)("inlineCode",{parentName:"p"},"value"),", otherwise returns ",(0,l.kt)("inlineCode",{parentName:"p"},"defaultValue"),"."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"Option.Some(2).getOr(1);\n// 2\n\nOption.None().getOr(1);\n// 1\n")),(0,l.kt)("h3",{id:"get"},".get()"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"Option.get(): A\n")),(0,l.kt)("p",null,"Returns the value contained in ",(0,l.kt)("inlineCode",{parentName:"p"},"Some(value)"),". Only usable within a ",(0,l.kt)("inlineCode",{parentName:"p"},"isSome()")," check."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"const value = option.get();\n// does not compile\n\nif (option.isSome()) {\n const value = option.get();\n // value\n}\n")),(0,l.kt)("h3",{id:"issome"},".isSome()"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"Option.isSome(): boolean\n")),(0,l.kt)("p",null,"Type guard. Checks if the option is ",(0,l.kt)("inlineCode",{parentName:"p"},"Some(value)")),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"Option.Some(2).isSome();\n// true\n\nOption.None().isSome();\n// false\n\nif (option.isSome()) {\n const value = option.get();\n}\n")),(0,l.kt)("h3",{id:"isnone"},".isNone()"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"Option.isNone(): boolean\n")),(0,l.kt)("p",null,"Type guard. Checks if the option is ",(0,l.kt)("inlineCode",{parentName:"p"},"None")),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"Option.Some(2).isNone();\n// false\n\nOption.None().isNone();\n// true\n")),(0,l.kt)("h3",{id:"tonull"},".toNull()"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"Option.toNull(): A | null\n")),(0,l.kt)("p",null,"Returns ",(0,l.kt)("inlineCode",{parentName:"p"},"null")," if the option is ",(0,l.kt)("inlineCode",{parentName:"p"},"None"),", returns the value otherwise"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"Option.Some(2).toNull();\n// 2\n\nOption.None().toNull();\n// null\n")),(0,l.kt)("h3",{id:"toundefined"},".toUndefined()"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"Option.toUndefined(): A | undefined\n")),(0,l.kt)("p",null,"Returns ",(0,l.kt)("inlineCode",{parentName:"p"},"undefined")," if the option is ",(0,l.kt)("inlineCode",{parentName:"p"},"None"),", returns the value otherwise"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"Option.Some(2).toUndefined();\n// 2\n\nOption.None().toUndefined();\n// undefined\n")),(0,l.kt)("h3",{id:"toresulterrorwhennone"},".toResult(errorWhenNone)"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"Option.toResult(valueWhenNone: E): Result\n")),(0,l.kt)("p",null,"Returns ",(0,l.kt)("inlineCode",{parentName:"p"},"Ok")," if the option is ",(0,l.kt)("inlineCode",{parentName:"p"},"Some"),", returns ",(0,l.kt)("inlineCode",{parentName:"p"},"Error")," otherwise"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},'const a = Option.Some(1).toResult("NotFound");\n// Ok<1>\n\nconst b = Option.None().toResult("NotFound");\n// Error<"NotFound">\n')),(0,l.kt)("h3",{id:"match"},".match()"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"Option.match(config: {\n Some: (value: A) => B;\n None: () => B;\n}): B\n")),(0,l.kt)("p",null,"Match the option state"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},'const valueToDisplay = option.match({\n Some: (value) => value,\n None: () => "No value",\n});\n// value | "No value"\n')),(0,l.kt)("h3",{id:"tapfunc"},".tap(func)"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"Option.tap(func: (option: Option) => unknown): Option\n")),(0,l.kt)("p",null,"Executes ",(0,l.kt)("inlineCode",{parentName:"p"},"func")," with ",(0,l.kt)("inlineCode",{parentName:"p"},"option"),", and returns ",(0,l.kt)("inlineCode",{parentName:"p"},"option"),". Useful for logging and debugging."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"option.tap(console.log).map((x) => x * 2);\n")),(0,l.kt)("h3",{id:"tapsomefunc"},".tapSome(func)"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"Option.tapSome(func: (option: A) => unknown): Option\n")),(0,l.kt)("p",null,"Executes ",(0,l.kt)("inlineCode",{parentName:"p"},"func")," with ",(0,l.kt)("inlineCode",{parentName:"p"},"option"),"'s value if ",(0,l.kt)("inlineCode",{parentName:"p"},"Some"),", and returns ",(0,l.kt)("inlineCode",{parentName:"p"},"option"),". Useful for logging and debugging."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"option.tapSome(console.log).map((x) => x * 2);\n")),(0,l.kt)("h2",{id:"statics"},"Statics"),(0,l.kt)("h3",{id:"optionisoptionvalue"},"Option.isOption(value)"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"isOption(value: unknown): boolean\n")),(0,l.kt)("p",null,"Type guard, checks if the provided value is an option."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"Option.isOption(Option.Some(1));\n// true\n\nOption.isOption([]);\n// false\n")),(0,l.kt)("h3",{id:"optionalloptions"},"Option.all(options)"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"all(options: Array>): Option>\n")),(0,l.kt)("p",null,'Turns an "array of options of value" into a "option of array of value".'),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"Option.all([Option.Some(1), Option.Some(2), Option.Some(3)]);\n// Some([1, 2, 3])\n\nOption.all([Option.None(), Option.Some(2), Option.Some(3)]);\n// None\n")),(0,l.kt)("h3",{id:"optionallfromdictoptions"},"Option.allFromDict(options)"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"allFromDict(options: Dict>): Option>\n")),(0,l.kt)("p",null,'Turns a "dict of options of value" into a "option of dict of value".'),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"Option.allFromDict({ a: Option.Some(1), b: Option.Some(2), c: Option.Some(3) });\n// Some({a: 1, b: 2, c: 3})\n\nOption.allFromDict({ a: Option.None(), b: Option.Some(2), c: Option.Some(3) });\n// None\n")),(0,l.kt)("h2",{id:"ts-pattern-interop"},"TS Pattern interop"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},'import { match, P } from "ts-pattern";\nimport { Option } from "@swan-io/boxed";\n\nmatch(myOption)\n .with(Option.P.Some(P.select()), (value) => console.log(value))\n .with(Option.P.None, () => "No value")\n .exhaustive();\n')),(0,l.kt)("h2",{id:"cheatsheet"},"Cheatsheet"),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Method"),(0,l.kt)("th",{parentName:"tr",align:null},"Input"),(0,l.kt)("th",{parentName:"tr",align:null},"Function input"),(0,l.kt)("th",{parentName:"tr",align:null},"Function output"),(0,l.kt)("th",{parentName:"tr",align:null},"Returned value"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"#mapf"},(0,l.kt)("inlineCode",{parentName:"a"},"map"))),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"Some(x)")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"x")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"y")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"Some(y)"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"#mapf"},(0,l.kt)("inlineCode",{parentName:"a"},"map"))),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"None()")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("em",{parentName:"td"},"not provided")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("em",{parentName:"td"},"not executed")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"None()"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"#flatmapf"},(0,l.kt)("inlineCode",{parentName:"a"},"flatMap"))),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"Some(x)")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"x")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"Some(y)")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"Some(y)"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"#flatmapf"},(0,l.kt)("inlineCode",{parentName:"a"},"flatMap"))),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"Some(x)")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"x")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"None()")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"None()"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"#flatmapf"},(0,l.kt)("inlineCode",{parentName:"a"},"flatMap"))),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"None()")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("em",{parentName:"td"},"not provided")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("em",{parentName:"td"},"not executed")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"None()"))))))}d.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/974039d5.4d46b89d.js b/assets/js/974039d5.4d46b89d.js new file mode 100644 index 0000000..6c551af --- /dev/null +++ b/assets/js/974039d5.4d46b89d.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[385],{3905:(e,a,t)=>{t.d(a,{Zo:()=>d,kt:()=>k});var n=t(7294);function l(e,a,t){return a in e?Object.defineProperty(e,a,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[a]=t,e}function r(e,a){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);a&&(n=n.filter((function(a){return Object.getOwnPropertyDescriptor(e,a).enumerable}))),t.push.apply(t,n)}return t}function s(e){for(var a=1;a=0||(l[t]=e[t]);return l}(e,a);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(l[t]=e[t])}return l}var o=n.createContext({}),p=function(e){var a=n.useContext(o),t=a;return e&&(t="function"==typeof e?e(a):s(s({},a),e)),t},d=function(e){var a=p(e.components);return n.createElement(o.Provider,{value:a},e.children)},c="mdxType",m={inlineCode:"code",wrapper:function(e){var a=e.children;return n.createElement(n.Fragment,{},a)}},u=n.forwardRef((function(e,a){var t=e.components,l=e.mdxType,r=e.originalType,o=e.parentName,d=i(e,["components","mdxType","originalType","parentName"]),c=p(t),u=l,k=c["".concat(o,".").concat(u)]||c[u]||m[u]||r;return t?n.createElement(k,s(s({ref:a},d),{},{components:t})):n.createElement(k,s({ref:a},d))}));function k(e,a){var t=arguments,l=a&&a.mdxType;if("string"==typeof e||l){var r=t.length,s=new Array(r);s[0]=u;var i={};for(var o in a)hasOwnProperty.call(a,o)&&(i[o]=a[o]);i.originalType=e,i[c]="string"==typeof e?e:l,s[1]=i;for(var p=2;p{t.r(a),t.d(a,{assets:()=>o,contentTitle:()=>s,default:()=>m,frontMatter:()=>r,metadata:()=>i,toc:()=>p});var n=t(7462),l=(t(7294),t(3905));const r={title:"AsyncData",sidebar_label:"AsyncData"},s=void 0,i={unversionedId:"async-data",id:"async-data",title:"AsyncData",description:"The AsyncData type enables representing asynchronous flows (e.g. requests). The type represents the state as a discriminating union, avoiding manual management for loading flows.",source:"@site/docs/async-data.md",sourceDirName:".",slug:"/async-data",permalink:"/boxed/async-data",draft:!1,editUrl:"https://github.com/swan-io/boxed/edit/main/docs/docs/async-data.md",tags:[],version:"current",frontMatter:{title:"AsyncData",sidebar_label:"AsyncData"},sidebar:"docs",previous:{title:"Result",permalink:"/boxed/result"},next:{title:"AsyncData helpers",permalink:"/boxed/async-data-result"}},o={},p=[{value:"Create an AsyncData value",id:"create-an-asyncdata-value",level:2},{value:"Methods",id:"methods",level:2},{value:".map(f)",id:"mapf",level:3},{value:".flatMap(f)",id:"flatmapf",level:3},{value:".getOr(defaultValue)",id:"getordefaultvalue",level:3},{value:".get()",id:"get",level:3},{value:".isDone()",id:"isdone",level:3},{value:".isLoading()",id:"isloading",level:3},{value:".isNotAsked()",id:"isnotasked",level:3},{value:".toOption()",id:"tooption",level:3},{value:".match()",id:"match",level:3},{value:".tap(func)",id:"tapfunc",level:3},{value:"Statics",id:"statics",level:2},{value:"AsyncData.isAsyncData(value)",id:"asyncdataisasyncdatavalue",level:3},{value:"AsyncData.all(asyncDatas)",id:"asyncdataallasyncdatas",level:3},{value:"AsyncData.allFromDict(asyncDatas)",id:"asyncdataallfromdictasyncdatas",level:3},{value:"TS Pattern interop",id:"ts-pattern-interop",level:2},{value:"Cheatsheet",id:"cheatsheet",level:2}],d={toc:p},c="wrapper";function m(e){let{components:a,...t}=e;return(0,l.kt)(c,(0,n.Z)({},d,t,{components:a,mdxType:"MDXLayout"}),(0,l.kt)("p",null,"The ",(0,l.kt)("inlineCode",{parentName:"p"},"AsyncData")," type enables representing asynchronous flows (e.g. requests). The type represents the state as a discriminating union, avoiding manual management for loading flows."),(0,l.kt)("p",null,(0,l.kt)("inlineCode",{parentName:"p"},"AsyncData")," can have three possible states:"),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("inlineCode",{parentName:"li"},"NotAsked")),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("inlineCode",{parentName:"li"},"Loading")),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("inlineCode",{parentName:"li"},"Done(value)"))),(0,l.kt)("h2",{id:"create-an-asyncdata-value"},"Create an AsyncData value"),(0,l.kt)("p",null,"To create an async data, use the ",(0,l.kt)("inlineCode",{parentName:"p"},"NotAsked"),", ",(0,l.kt)("inlineCode",{parentName:"p"},"Loading")," and ",(0,l.kt)("inlineCode",{parentName:"p"},"Done")," constructors:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},'import { AsyncData } from "@swan-io/boxed";\n\nconst notAsked = AsyncData.NotAsked();\n\nconst loading = AsyncData.Loading();\n\nconst done = AsyncData.Done(1);\n')),(0,l.kt)("admonition",{title:"Since v3.0.0",type:"tip"},(0,l.kt)("p",{parentName:"admonition"},(0,l.kt)("inlineCode",{parentName:"p"},"AsyncData")," values are referentially equal if they contain the same value, meaning that ",(0,l.kt)("inlineCode",{parentName:"p"},"AsyncData.Done(1) === AsyncData.Done(1)"),".")),(0,l.kt)("h2",{id:"methods"},"Methods"),(0,l.kt)("p",null,"The async data type provides a few manipulation functions:"),(0,l.kt)("h3",{id:"mapf"},".map(f)"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"AsyncData.map(f: (value: A) => B): AsyncData\n")),(0,l.kt)("p",null,"If the asyncData is ",(0,l.kt)("inlineCode",{parentName:"p"},"Done(value)")," returns ",(0,l.kt)("inlineCode",{parentName:"p"},"Done(f(value))"),", otherwise returns the async data."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"AsyncData.Done(2).map((x) => x * 2);\n// AsyncData.Done<4>\n\nAsyncData.Loading().map((x) => x * 2);\n// AsyncData.Loading\n\nAsyncData.NotAsked().map((x) => x * 2);\n// AsyncData.NotAsked\n")),(0,l.kt)("h3",{id:"flatmapf"},".flatMap(f)"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"AsyncData.flatMap(f: (value: A) => AsyncData): AsyncData\n")),(0,l.kt)("p",null,"If the asyncData is ",(0,l.kt)("inlineCode",{parentName:"p"},"Done(value)")," returns ",(0,l.kt)("inlineCode",{parentName:"p"},"f(value)"),", otherwise returns the async data."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"AsyncData.Done(3).flatMap((x) =>\n x > 2 ? AsyncData.NotAsked() : AsyncData.Done(2),\n);\n// AsyncData.NotAsked\n\nAsyncData.Done(1).flatMap((x) =>\n x > 2 ? AsyncData.NotAsked() : AsyncData.Done(2),\n);\n// AsyncData.Done<2>\n\nAsyncData.NotAsked().flatMap((x) =>\n x > 2 ? AsyncData.NotAsked() : AsyncData.Done(2),\n);\n// AsyncData.NotAsked\n\nAsyncData.Loading().flatMap((x) =>\n x > 2 ? AsyncData.NotAsked() : AsyncData.Done(2),\n);\n// AsyncData.Loading\n")),(0,l.kt)("h3",{id:"getordefaultvalue"},".getOr(defaultValue)"),(0,l.kt)("blockquote",null,(0,l.kt)("p",{parentName:"blockquote"},"Alias: ",(0,l.kt)("inlineCode",{parentName:"p"},"getWithDefault"))),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"AsyncData.getOr(defaultValue: A): A\n")),(0,l.kt)("p",null,"If the async data is ",(0,l.kt)("inlineCode",{parentName:"p"},"Done(value)")," returns ",(0,l.kt)("inlineCode",{parentName:"p"},"value"),", otherwise returns ",(0,l.kt)("inlineCode",{parentName:"p"},"defaultValue"),"."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"AsyncData.Done(2).getOr(1);\n// 2\n\nAsyncData.Loading().getOr(1);\n// 1\n\nAsyncData.NotAsked().getOr(1);\n// 1\n")),(0,l.kt)("h3",{id:"get"},".get()"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"AsyncData.get(): A\n")),(0,l.kt)("p",null,"Returns the value contained in ",(0,l.kt)("inlineCode",{parentName:"p"},"Done(value)"),". Only usable within a ",(0,l.kt)("inlineCode",{parentName:"p"},"isDone()")," check."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"const value = asyncData.get();\n// does not compile\n\nif (asyncData.isDone()) {\n const value = asyncData.get();\n // value\n}\n")),(0,l.kt)("h3",{id:"isdone"},".isDone()"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"AsyncData.isDone(): boolean\n")),(0,l.kt)("p",null,"Type guard. Checks if the option is ",(0,l.kt)("inlineCode",{parentName:"p"},"Done(value)")),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"AsyncData.Done(2).isDone();\n// true\n\nAsyncData.Loading().isDone();\n// false\n\nAsyncData.NotAsked().isDone();\n// false\n\nif (asyncData.isDone()) {\n const value = asyncData.get();\n}\n")),(0,l.kt)("h3",{id:"isloading"},".isLoading()"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"AsyncData.isLoading(): boolean\n")),(0,l.kt)("p",null,"Type guard. Checks if the option is ",(0,l.kt)("inlineCode",{parentName:"p"},"Loading")),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"AsyncData.Done(2).isLoading();\n// false\n\nAsyncData.Loading().isLoading();\n// true\n\nAsyncData.NotAsked().isLoading();\n// false\n")),(0,l.kt)("h3",{id:"isnotasked"},".isNotAsked()"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"AsyncData.isNotAsked(): boolean\n")),(0,l.kt)("p",null,"Type guard. Checks if the option is ",(0,l.kt)("inlineCode",{parentName:"p"},"NotAsked")),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"AsyncData.Done(2).isNotAsked();\n// false\n\nAsyncData.Loading().isNotAsked();\n// false\n\nAsyncData.NotAsked().isNotAsked();\n// true\n")),(0,l.kt)("h3",{id:"tooption"},".toOption()"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"AsyncData.toOption(): Option\n")),(0,l.kt)("p",null,"If the result is ",(0,l.kt)("inlineCode",{parentName:"p"},"Done(value)")," returns ",(0,l.kt)("inlineCode",{parentName:"p"},"Some(value)"),", otherwise returns ",(0,l.kt)("inlineCode",{parentName:"p"},"None"),"."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"Result.Done(2).toOption();\n// Option.Some<2>\n\nResult.Loading().toOption();\n// Option.None\n\nResult.NotAsked().toOption();\n// Option.None\n")),(0,l.kt)("h3",{id:"match"},".match()"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"AsyncData.match(config: {\n Done: (value: A) => B;\n Loading: () => B;\n NotAsked: () => B;\n}): B;\n")),(0,l.kt)("p",null,"Match the async data state"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},'const valueToDisplay = result.match({\n Done: (value) => value,\n Loading: () => "Loading ...",\n NotAsked: () => "",\n});\n')),(0,l.kt)("h3",{id:"tapfunc"},".tap(func)"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"AsyncData.tap(func: (asyncData: AsyncData) => unknown): AsyncData\n")),(0,l.kt)("p",null,"Executes ",(0,l.kt)("inlineCode",{parentName:"p"},"func")," with ",(0,l.kt)("inlineCode",{parentName:"p"},"asyncData"),", and returns ",(0,l.kt)("inlineCode",{parentName:"p"},"asyncData"),". Useful for logging and debugging."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"asyncData.tap(console.log).map((x) => x * 2);\n")),(0,l.kt)("h2",{id:"statics"},"Statics"),(0,l.kt)("h3",{id:"asyncdataisasyncdatavalue"},"AsyncData.isAsyncData(value)"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"isAsyncData(value: unknown): boolean\n")),(0,l.kt)("p",null,"Type guard, checks if the provided value is an asyncData."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"AsyncData.isAsyncData(AsyncData.Done(1));\n// true\n\nAsyncData.isAsyncData([]);\n// false\n")),(0,l.kt)("h3",{id:"asyncdataallasyncdatas"},"AsyncData.all(asyncDatas)"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"all(asyncDatas: Array>): AsyncData>\n")),(0,l.kt)("p",null,'Turns an "array of asyncDatas of value" into a "asyncData of array of value".'),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"AsyncData.all([AsyncData.Done(1), AsyncData.Done(2), AsyncData.Done(3)]);\n// AsyncData.Done<[1, 2, 3]>\n\nAsyncData.all([Result.NotAsked(), AsyncData.Done(2), AsyncData.Done(3)]);\n// AsyncData.NotAsked\n\nAsyncData.all([Result.Loading(), AsyncData.Done(2), AsyncData.Done(3)]);\n// AsyncData.Loading\n")),(0,l.kt)("h3",{id:"asyncdataallfromdictasyncdatas"},"AsyncData.allFromDict(asyncDatas)"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"allFromDict(asyncDatas: Dict>): AsyncData>\n")),(0,l.kt)("p",null,'Turns a "dict of asyncDatas of value" into a "asyncData of dict of value".'),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"AsyncData.allFromDict({\n a: AsyncData.Done(1),\n b: AsyncData.Done(2),\n c: AsyncData.Done(3),\n});\n// AsyncData.Done<{a: 1, b: 2, c: 3}>\n\nAsyncData.allFromDict({\n a: Result.NotAsked(),\n b: AsyncData.Done(2),\n c: AsyncData.Done(3),\n});\n// AsyncData.NotAsked\n\nAsyncData.allFromDict({\n a: Result.Loading(),\n b: AsyncData.Done(2),\n c: AsyncData.Done(3),\n});\n// AsyncData.Loading\n")),(0,l.kt)("h2",{id:"ts-pattern-interop"},"TS Pattern interop"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},'import { match, P } from "ts-pattern";\nimport { AsyncData } from "@swan-io/boxed";\n\nmatch(asyncData)\n .with(AsyncData.P.Done(P.select()), (value) => console.log(value))\n .with(AsyncData.P.Loading, () => "Loading ...")\n .with(AsyncData.P.NotAsked, () => "")\n .exhaustive();\n')),(0,l.kt)("h2",{id:"cheatsheet"},"Cheatsheet"),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Method"),(0,l.kt)("th",{parentName:"tr",align:null},"Input"),(0,l.kt)("th",{parentName:"tr",align:null},"Function input"),(0,l.kt)("th",{parentName:"tr",align:null},"Function output"),(0,l.kt)("th",{parentName:"tr",align:null},"Returned value"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"#mapf"},(0,l.kt)("inlineCode",{parentName:"a"},"map"))),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"Done(x)")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"x")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"y")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"Done(y)"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"#mapf"},(0,l.kt)("inlineCode",{parentName:"a"},"map"))),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"Loading()")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("em",{parentName:"td"},"not provided")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("em",{parentName:"td"},"not executed")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"Loading()"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"#mapf"},(0,l.kt)("inlineCode",{parentName:"a"},"map"))),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"NotAsked()")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("em",{parentName:"td"},"not provided")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("em",{parentName:"td"},"not executed")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"NotAsked()"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"#flatmapf"},(0,l.kt)("inlineCode",{parentName:"a"},"flatMap"))),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"Done(x)")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"x")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"Done(y)")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"Done(y)"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"#flatmapf"},(0,l.kt)("inlineCode",{parentName:"a"},"flatMap"))),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"Done(x)")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"x")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"Loading()")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"Loading()"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"#flatmapf"},(0,l.kt)("inlineCode",{parentName:"a"},"flatMap"))),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"Done(x)")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"x")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"NotAsked()")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"NotAsked()"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"#flatmapf"},(0,l.kt)("inlineCode",{parentName:"a"},"flatMap"))),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"Loading()")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("em",{parentName:"td"},"not provided")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("em",{parentName:"td"},"not executed")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"Loading()"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"#flatmapf"},(0,l.kt)("inlineCode",{parentName:"a"},"flatMap"))),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"NotAsked()")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("em",{parentName:"td"},"not provided")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("em",{parentName:"td"},"not executed")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"NotAsked()"))))))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/974039d5.69facaf0.js b/assets/js/974039d5.69facaf0.js deleted file mode 100644 index 27e100d..0000000 --- a/assets/js/974039d5.69facaf0.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[385],{3905:(e,a,t)=>{t.d(a,{Zo:()=>d,kt:()=>k});var n=t(7294);function l(e,a,t){return a in e?Object.defineProperty(e,a,{value:t,enumerable:!0,configurable:!0,writable:!0}):e[a]=t,e}function r(e,a){var t=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);a&&(n=n.filter((function(a){return Object.getOwnPropertyDescriptor(e,a).enumerable}))),t.push.apply(t,n)}return t}function s(e){for(var a=1;a=0||(l[t]=e[t]);return l}(e,a);if(Object.getOwnPropertySymbols){var r=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,t)&&(l[t]=e[t])}return l}var o=n.createContext({}),p=function(e){var a=n.useContext(o),t=a;return e&&(t="function"==typeof e?e(a):s(s({},a),e)),t},d=function(e){var a=p(e.components);return n.createElement(o.Provider,{value:a},e.children)},c="mdxType",m={inlineCode:"code",wrapper:function(e){var a=e.children;return n.createElement(n.Fragment,{},a)}},u=n.forwardRef((function(e,a){var t=e.components,l=e.mdxType,r=e.originalType,o=e.parentName,d=i(e,["components","mdxType","originalType","parentName"]),c=p(t),u=l,k=c["".concat(o,".").concat(u)]||c[u]||m[u]||r;return t?n.createElement(k,s(s({ref:a},d),{},{components:t})):n.createElement(k,s({ref:a},d))}));function k(e,a){var t=arguments,l=a&&a.mdxType;if("string"==typeof e||l){var r=t.length,s=new Array(r);s[0]=u;var i={};for(var o in a)hasOwnProperty.call(a,o)&&(i[o]=a[o]);i.originalType=e,i[c]="string"==typeof e?e:l,s[1]=i;for(var p=2;p{t.r(a),t.d(a,{assets:()=>o,contentTitle:()=>s,default:()=>m,frontMatter:()=>r,metadata:()=>i,toc:()=>p});var n=t(7462),l=(t(7294),t(3905));const r={title:"AsyncData",sidebar_label:"AsyncData"},s=void 0,i={unversionedId:"async-data",id:"async-data",title:"AsyncData",description:"The AsyncData type enables representing asynchronous flows (e.g. requests). The type represents the state as a discriminating union, avoiding manual management for loading flows.",source:"@site/docs/async-data.md",sourceDirName:".",slug:"/async-data",permalink:"/boxed/async-data",draft:!1,editUrl:"https://github.com/swan-io/boxed/edit/main/docs/docs/async-data.md",tags:[],version:"current",frontMatter:{title:"AsyncData",sidebar_label:"AsyncData"},sidebar:"docs",previous:{title:"Result",permalink:"/boxed/result"},next:{title:"AsyncData helpers",permalink:"/boxed/async-data-result"}},o={},p=[{value:"Create an AsyncData value",id:"create-an-asyncdata-value",level:2},{value:"Methods",id:"methods",level:2},{value:".map(f)",id:"mapf",level:3},{value:".flatMap(f)",id:"flatmapf",level:3},{value:".getOr(defaultValue)",id:"getordefaultvalue",level:3},{value:".get()",id:"get",level:3},{value:".isDone()",id:"isdone",level:3},{value:".isLoading()",id:"isloading",level:3},{value:".isNotAsked()",id:"isnotasked",level:3},{value:".toOption()",id:"tooption",level:3},{value:".match()",id:"match",level:3},{value:".tap(func)",id:"tapfunc",level:3},{value:"Statics",id:"statics",level:2},{value:"AsyncData.isAsyncData(value)",id:"asyncdataisasyncdatavalue",level:3},{value:"AsyncData.all(asyncDatas)",id:"asyncdataallasyncdatas",level:3},{value:"AsyncData.allFromDict(asyncDatas)",id:"asyncdataallfromdictasyncdatas",level:3},{value:"TS Pattern interop",id:"ts-pattern-interop",level:2},{value:"Cheatsheet",id:"cheatsheet",level:2}],d={toc:p},c="wrapper";function m(e){let{components:a,...t}=e;return(0,l.kt)(c,(0,n.Z)({},d,t,{components:a,mdxType:"MDXLayout"}),(0,l.kt)("p",null,"The ",(0,l.kt)("inlineCode",{parentName:"p"},"AsyncData")," type enables representing asynchronous flows (e.g. requests). The type represents the state as a discriminating union, avoiding manual management for loading flows."),(0,l.kt)("p",null,(0,l.kt)("inlineCode",{parentName:"p"},"AsyncData")," can have three possible states:"),(0,l.kt)("ul",null,(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("inlineCode",{parentName:"li"},"NotAsked")),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("inlineCode",{parentName:"li"},"Loading")),(0,l.kt)("li",{parentName:"ul"},(0,l.kt)("inlineCode",{parentName:"li"},"Done(value)"))),(0,l.kt)("h2",{id:"create-an-asyncdata-value"},"Create an AsyncData value"),(0,l.kt)("p",null,"To create an async data, use the ",(0,l.kt)("inlineCode",{parentName:"p"},"NotAsked"),", ",(0,l.kt)("inlineCode",{parentName:"p"},"Loading")," and ",(0,l.kt)("inlineCode",{parentName:"p"},"Done")," constructors:"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},'import { AsyncData } from "@swan-io/boxed";\n\nconst notAsked = AsyncData.NotAsked();\n\nconst loading = AsyncData.Loading();\n\nconst done = AsyncData.Done(1);\n')),(0,l.kt)("h2",{id:"methods"},"Methods"),(0,l.kt)("p",null,"The async data type provides a few manipulation functions:"),(0,l.kt)("h3",{id:"mapf"},".map(f)"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"AsyncData.map(f: (value: A) => B): AsyncData\n")),(0,l.kt)("p",null,"If the asyncData is ",(0,l.kt)("inlineCode",{parentName:"p"},"Done(value)")," returns ",(0,l.kt)("inlineCode",{parentName:"p"},"Done(f(value))"),", otherwise returns the async data."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"AsyncData.Done(2).map((x) => x * 2);\n// AsyncData.Done<4>\n\nAsyncData.Loading().map((x) => x * 2);\n// AsyncData.Loading\n\nAsyncData.NotAsked().map((x) => x * 2);\n// AsyncData.NotAsked\n")),(0,l.kt)("h3",{id:"flatmapf"},".flatMap(f)"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"AsyncData.flatMap(f: (value: A) => AsyncData): AsyncData\n")),(0,l.kt)("p",null,"If the asyncData is ",(0,l.kt)("inlineCode",{parentName:"p"},"Done(value)")," returns ",(0,l.kt)("inlineCode",{parentName:"p"},"f(value)"),", otherwise returns the async data."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"AsyncData.Done(3).flatMap((x) =>\n x > 2 ? AsyncData.NotAsked() : AsyncData.Done(2),\n);\n// AsyncData.NotAsked\n\nAsyncData.Done(1).flatMap((x) =>\n x > 2 ? AsyncData.NotAsked() : AsyncData.Done(2),\n);\n// AsyncData.Done<2>\n\nAsyncData.NotAsked().flatMap((x) =>\n x > 2 ? AsyncData.NotAsked() : AsyncData.Done(2),\n);\n// AsyncData.NotAsked\n\nAsyncData.Loading().flatMap((x) =>\n x > 2 ? AsyncData.NotAsked() : AsyncData.Done(2),\n);\n// AsyncData.Loading\n")),(0,l.kt)("h3",{id:"getordefaultvalue"},".getOr(defaultValue)"),(0,l.kt)("blockquote",null,(0,l.kt)("p",{parentName:"blockquote"},"Alias: ",(0,l.kt)("inlineCode",{parentName:"p"},"getWithDefault"))),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"AsyncData.getOr(defaultValue: A): A\n")),(0,l.kt)("p",null,"If the async data is ",(0,l.kt)("inlineCode",{parentName:"p"},"Done(value)")," returns ",(0,l.kt)("inlineCode",{parentName:"p"},"value"),", otherwise returns ",(0,l.kt)("inlineCode",{parentName:"p"},"defaultValue"),"."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"AsyncData.Done(2).getOr(1);\n// 2\n\nAsyncData.Loading().getOr(1);\n// 1\n\nAsyncData.NotAsked().getOr(1);\n// 1\n")),(0,l.kt)("h3",{id:"get"},".get()"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"AsyncData.get(): A\n")),(0,l.kt)("p",null,"Returns the value contained in ",(0,l.kt)("inlineCode",{parentName:"p"},"Done(value)"),". Only usable within a ",(0,l.kt)("inlineCode",{parentName:"p"},"isDone()")," check."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"const value = asyncData.get();\n// does not compile\n\nif (asyncData.isDone()) {\n const value = asyncData.get();\n // value\n}\n")),(0,l.kt)("h3",{id:"isdone"},".isDone()"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"AsyncData.isDone(): boolean\n")),(0,l.kt)("p",null,"Type guard. Checks if the option is ",(0,l.kt)("inlineCode",{parentName:"p"},"Done(value)")),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"AsyncData.Done(2).isDone();\n// true\n\nAsyncData.Loading().isDone();\n// false\n\nAsyncData.NotAsked().isDone();\n// false\n\nif (asyncData.isDone()) {\n const value = asyncData.get();\n}\n")),(0,l.kt)("h3",{id:"isloading"},".isLoading()"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"AsyncData.isLoading(): boolean\n")),(0,l.kt)("p",null,"Type guard. Checks if the option is ",(0,l.kt)("inlineCode",{parentName:"p"},"Loading")),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"AsyncData.Done(2).isLoading();\n// false\n\nAsyncData.Loading().isLoading();\n// true\n\nAsyncData.NotAsked().isLoading();\n// false\n")),(0,l.kt)("h3",{id:"isnotasked"},".isNotAsked()"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"AsyncData.isNotAsked(): boolean\n")),(0,l.kt)("p",null,"Type guard. Checks if the option is ",(0,l.kt)("inlineCode",{parentName:"p"},"NotAsked")),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"AsyncData.Done(2).isNotAsked();\n// false\n\nAsyncData.Loading().isNotAsked();\n// false\n\nAsyncData.NotAsked().isNotAsked();\n// true\n")),(0,l.kt)("h3",{id:"tooption"},".toOption()"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"AsyncData.toOption(): Option\n")),(0,l.kt)("p",null,"If the result is ",(0,l.kt)("inlineCode",{parentName:"p"},"Done(value)")," returns ",(0,l.kt)("inlineCode",{parentName:"p"},"Some(value)"),", otherwise returns ",(0,l.kt)("inlineCode",{parentName:"p"},"None"),"."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"Result.Done(2).toOption();\n// Option.Some<2>\n\nResult.Loading().toOption();\n// Option.None\n\nResult.NotAsked().toOption();\n// Option.None\n")),(0,l.kt)("h3",{id:"match"},".match()"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"AsyncData.match(config: {\n Done: (value: A) => B;\n Loading: () => B;\n NotAsked: () => B;\n}): B;\n")),(0,l.kt)("p",null,"Match the async data state"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},'const valueToDisplay = result.match({\n Done: (value) => value,\n Loading: () => "Loading ...",\n NotAsked: () => "",\n});\n')),(0,l.kt)("h3",{id:"tapfunc"},".tap(func)"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"AsyncData.tap(func: (asyncData: AsyncData) => unknown): AsyncData\n")),(0,l.kt)("p",null,"Executes ",(0,l.kt)("inlineCode",{parentName:"p"},"func")," with ",(0,l.kt)("inlineCode",{parentName:"p"},"asyncData"),", and returns ",(0,l.kt)("inlineCode",{parentName:"p"},"asyncData"),". Useful for logging and debugging."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"asyncData.tap(console.log).map((x) => x * 2);\n")),(0,l.kt)("h2",{id:"statics"},"Statics"),(0,l.kt)("h3",{id:"asyncdataisasyncdatavalue"},"AsyncData.isAsyncData(value)"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"isAsyncData(value: unknown): boolean\n")),(0,l.kt)("p",null,"Type guard, checks if the provided value is an asyncData."),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"AsyncData.isAsyncData(AsyncData.Done(1));\n// true\n\nAsyncData.isAsyncData([]);\n// false\n")),(0,l.kt)("h3",{id:"asyncdataallasyncdatas"},"AsyncData.all(asyncDatas)"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"all(asyncDatas: Array>): AsyncData>\n")),(0,l.kt)("p",null,'Turns an "array of asyncDatas of value" into a "asyncData of array of value".'),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"AsyncData.all([AsyncData.Done(1), AsyncData.Done(2), AsyncData.Done(3)]);\n// AsyncData.Done<[1, 2, 3]>\n\nAsyncData.all([Result.NotAsked(), AsyncData.Done(2), AsyncData.Done(3)]);\n// AsyncData.NotAsked\n\nAsyncData.all([Result.Loading(), AsyncData.Done(2), AsyncData.Done(3)]);\n// AsyncData.Loading\n")),(0,l.kt)("h3",{id:"asyncdataallfromdictasyncdatas"},"AsyncData.allFromDict(asyncDatas)"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts"},"allFromDict(asyncDatas: Dict>): AsyncData>\n")),(0,l.kt)("p",null,'Turns a "dict of asyncDatas of value" into a "asyncData of dict of value".'),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"AsyncData.allFromDict({\n a: AsyncData.Done(1),\n b: AsyncData.Done(2),\n c: AsyncData.Done(3),\n});\n// AsyncData.Done<{a: 1, b: 2, c: 3}>\n\nAsyncData.allFromDict({\n a: Result.NotAsked(),\n b: AsyncData.Done(2),\n c: AsyncData.Done(3),\n});\n// AsyncData.NotAsked\n\nAsyncData.allFromDict({\n a: Result.Loading(),\n b: AsyncData.Done(2),\n c: AsyncData.Done(3),\n});\n// AsyncData.Loading\n")),(0,l.kt)("h2",{id:"ts-pattern-interop"},"TS Pattern interop"),(0,l.kt)("pre",null,(0,l.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},'import { match, P } from "ts-pattern";\nimport { AsyncData } from "@swan-io/boxed";\n\nmatch(asyncData)\n .with(AsyncData.P.Done(P.select()), (value) => console.log(value))\n .with(AsyncData.P.Loading, () => "Loading ...")\n .with(AsyncData.P.NotAsked, () => "")\n .exhaustive();\n')),(0,l.kt)("h2",{id:"cheatsheet"},"Cheatsheet"),(0,l.kt)("table",null,(0,l.kt)("thead",{parentName:"table"},(0,l.kt)("tr",{parentName:"thead"},(0,l.kt)("th",{parentName:"tr",align:null},"Method"),(0,l.kt)("th",{parentName:"tr",align:null},"Input"),(0,l.kt)("th",{parentName:"tr",align:null},"Function input"),(0,l.kt)("th",{parentName:"tr",align:null},"Function output"),(0,l.kt)("th",{parentName:"tr",align:null},"Returned value"))),(0,l.kt)("tbody",{parentName:"table"},(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"#mapf"},(0,l.kt)("inlineCode",{parentName:"a"},"map"))),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"Done(x)")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"x")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"y")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"Done(y)"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"#mapf"},(0,l.kt)("inlineCode",{parentName:"a"},"map"))),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"Loading()")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("em",{parentName:"td"},"not provided")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("em",{parentName:"td"},"not executed")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"Loading()"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"#mapf"},(0,l.kt)("inlineCode",{parentName:"a"},"map"))),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"NotAsked()")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("em",{parentName:"td"},"not provided")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("em",{parentName:"td"},"not executed")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"NotAsked()"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"#flatmapf"},(0,l.kt)("inlineCode",{parentName:"a"},"flatMap"))),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"Done(x)")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"x")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"Done(y)")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"Done(y)"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"#flatmapf"},(0,l.kt)("inlineCode",{parentName:"a"},"flatMap"))),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"Done(x)")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"x")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"Loading()")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"Loading()"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"#flatmapf"},(0,l.kt)("inlineCode",{parentName:"a"},"flatMap"))),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"Done(x)")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"x")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"NotAsked()")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"NotAsked()"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"#flatmapf"},(0,l.kt)("inlineCode",{parentName:"a"},"flatMap"))),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"Loading()")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("em",{parentName:"td"},"not provided")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("em",{parentName:"td"},"not executed")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"Loading()"))),(0,l.kt)("tr",{parentName:"tbody"},(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("a",{parentName:"td",href:"#flatmapf"},(0,l.kt)("inlineCode",{parentName:"a"},"flatMap"))),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"NotAsked()")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("em",{parentName:"td"},"not provided")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("em",{parentName:"td"},"not executed")),(0,l.kt)("td",{parentName:"tr",align:null},(0,l.kt)("inlineCode",{parentName:"td"},"NotAsked()"))))))}m.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/ace312b1.936ef1cd.js b/assets/js/ace312b1.936ef1cd.js new file mode 100644 index 0000000..08d7917 --- /dev/null +++ b/assets/js/ace312b1.936ef1cd.js @@ -0,0 +1 @@ +"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[527],{3905:(e,t,r)=>{r.d(t,{Zo:()=>u,kt:()=>c});var n=r(7294);function a(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function l(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function o(e){for(var t=1;t=0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var s=n.createContext({}),p=function(e){var t=n.useContext(s),r=t;return e&&(r="function"==typeof e?e(t):o(o({},t),e)),r},u=function(e){var t=p(e.components);return n.createElement(s.Provider,{value:t},e.children)},m="mdxType",k={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},d=n.forwardRef((function(e,t){var r=e.components,a=e.mdxType,l=e.originalType,s=e.parentName,u=i(e,["components","mdxType","originalType","parentName"]),m=p(r),d=a,c=m["".concat(s,".").concat(d)]||m[d]||k[d]||l;return r?n.createElement(c,o(o({ref:t},u),{},{components:r})):n.createElement(c,o({ref:t},u))}));function c(e,t){var r=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var l=r.length,o=new Array(l);o[0]=d;var i={};for(var s in t)hasOwnProperty.call(t,s)&&(i[s]=t[s]);i.originalType=e,i[m]="string"==typeof e?e:a,o[1]=i;for(var p=2;p{r.r(t),r.d(t,{assets:()=>s,contentTitle:()=>o,default:()=>k,frontMatter:()=>l,metadata:()=>i,toc:()=>p});var n=r(7462),a=(r(7294),r(3905));const l={title:"Result",sidebar_label:"Result"},o=void 0,i={unversionedId:"result",id:"result",title:"Result",description:"The Result can replace exception flows.",source:"@site/docs/result.md",sourceDirName:".",slug:"/result",permalink:"/boxed/result",draft:!1,editUrl:"https://github.com/swan-io/boxed/edit/main/docs/docs/result.md",tags:[],version:"current",frontMatter:{title:"Result",sidebar_label:"Result"},sidebar:"docs",previous:{title:"Option",permalink:"/boxed/option"},next:{title:"AsyncData",permalink:"/boxed/async-data"}},s={},p=[{value:"Create a Result value",id:"create-a-result-value",level:2},{value:"Methods",id:"methods",level:2},{value:".map(f)",id:"mapf",level:3},{value:".mapError(f)",id:"maperrorf",level:3},{value:".flatMap(f)",id:"flatmapf",level:3},{value:".flatMapError(f)",id:"flatmaperrorf",level:3},{value:".getOr(defaultValue)",id:"getordefaultvalue",level:3},{value:".get()",id:"get",level:3},{value:".getError()",id:"geterror",level:3},{value:".isOk()",id:"isok",level:3},{value:".isError()",id:"iserror",level:3},{value:".toOption()",id:"tooption",level:3},{value:".match()",id:"match",level:3},{value:".tap(func)",id:"tapfunc",level:3},{value:".tapOk(func)",id:"tapokfunc",level:3},{value:".tapError(func)",id:"taperrorfunc",level:3},{value:"Statics",id:"statics",level:2},{value:"Result.isResult(value)",id:"resultisresultvalue",level:3},{value:"Result.all(results)",id:"resultallresults",level:3},{value:"Result.allFromDict(results)",id:"resultallfromdictresults",level:3},{value:"Result.fromExecution(() => value)",id:"resultfromexecution--value",level:3},{value:"Result.fromPromise(promise)",id:"resultfrompromisepromise",level:3},{value:"Result.fromOption(option, valueIfNone)",id:"resultfromoptionoption-valueifnone",level:3},{value:"TS Pattern interop",id:"ts-pattern-interop",level:2},{value:"Cheatsheet",id:"cheatsheet",level:2}],u={toc:p},m="wrapper";function k(e){let{components:t,...r}=e;return(0,a.kt)(m,(0,n.Z)({},u,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"Result")," can replace exception flows."),(0,a.kt)("p",null,"Exceptions can be tricky to handle: there's nothing in the type system that tracks if an error has been handled, which is error prone, and adds to your mental overhead. ",(0,a.kt)("inlineCode",{parentName:"p"},"Result")," helps as it ",(0,a.kt)("strong",{parentName:"p"},"makes the value hold the success state"),", making it dead-simple to track with a type-system."),(0,a.kt)("p",null,"Just like the ",(0,a.kt)("inlineCode",{parentName:"p"},"Option")," type, the ",(0,a.kt)("inlineCode",{parentName:"p"},"Result")," type is a box that can have two states:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"Ok(value)")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"Error(error)"))),(0,a.kt)("h2",{id:"create-a-result-value"},"Create a Result value"),(0,a.kt)("p",null,"To create a result, use the ",(0,a.kt)("inlineCode",{parentName:"p"},"Ok")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"Error")," constructors:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'import { Result } from "@swan-io/boxed";\n\nconst ok = Result.Ok(1);\n\nconst notOk = Result.Error("something happened");\n')),(0,a.kt)("p",null,"You can convert an option to a ",(0,a.kt)("inlineCode",{parentName:"p"},"Result"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'import { Result, Option } from "@swan-io/boxed";\n\nconst a = Result.fromOption(Option.Some(1), "NotFound");\n// Ok<1>\n\nconst b = Result.fromOption(Option.None(), "NotFound");\n// Error<"NotFound">\n')),(0,a.kt)("p",null,"You get interop with exceptions and promises:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'// Let\'s say you have some function that throws an error\nconst init = (id: string) => {\n if (id.length !== 24) {\n throw new Error();\n }\n return new Client({ id });\n};\n\nconst result = Result.fromExecution(() => init(id));\n// Here, result will either be:\n// - Ok(client)\n// - Error(error)\n\n// It works with promises too:\n\nconst value = await Result.fromPromise(() => fetch("/api"));\n// `value` will either be:\n// - Ok(res)\n// - Error(error)\n')),(0,a.kt)("admonition",{title:"Since v3.0.0",type:"tip"},(0,a.kt)("p",{parentName:"admonition"},(0,a.kt)("inlineCode",{parentName:"p"},"Result")," values are referentially equal if they contain the same value, meaning that ",(0,a.kt)("inlineCode",{parentName:"p"},"Result.Ok(1) === Result.Ok(1)")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"Result.Error(1) === Result.Error(1)"),".")),(0,a.kt)("h2",{id:"methods"},"Methods"),(0,a.kt)("p",null,"The result type provides a few manipulation functions:"),(0,a.kt)("h3",{id:"mapf"},".map(f)"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"Result.map(f: (value: A) => B): Result\n")),(0,a.kt)("p",null,"If the result is ",(0,a.kt)("inlineCode",{parentName:"p"},"Ok(value)")," returns ",(0,a.kt)("inlineCode",{parentName:"p"},"Ok(f(value))"),", otherwise returns ",(0,a.kt)("inlineCode",{parentName:"p"},"Error(error)"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"Result.Ok(2).map((x) => x * 2);\n// Result.Ok<4>\n\nResult.Ok(2).map((x) => Result.Ok(x * 2));\n// Result.Ok>\n")),(0,a.kt)("h3",{id:"maperrorf"},".mapError(f)"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"Result.mapError(f: (value: E) => F): Result\n")),(0,a.kt)("p",null,"If the result is ",(0,a.kt)("inlineCode",{parentName:"p"},"Error(error)")," returns ",(0,a.kt)("inlineCode",{parentName:"p"},"Error(f(error))"),", otherwise returns ",(0,a.kt)("inlineCode",{parentName:"p"},"Ok(value)"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"Result.Error(2).mapError((x) => x * 2);\n// Result.Error<4>\n\nResult.Error(2).mapError((x) => Result.Ok(x * 2));\n// Result.Error>\n")),(0,a.kt)("h3",{id:"flatmapf"},".flatMap(f)"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"Result.flatMap(f: (value: A) => Result): Result\n")),(0,a.kt)("p",null,"If the result is ",(0,a.kt)("inlineCode",{parentName:"p"},"Ok(value)")," returns ",(0,a.kt)("inlineCode",{parentName:"p"},"f(value)"),", otherwise returns ",(0,a.kt)("inlineCode",{parentName:"p"},"Error(error)"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},'Result.Ok(1).flatMap((x) =>\n x > 2 ? Result.Error("some error") : Result.Ok(2),\n);\n// Result.Ok<2>\n\nResult.Ok(3).flatMap((x) =>\n x > 2 ? Result.Error("some error") : Result.Ok(2),\n);\n// Result.Error<"some error">\n\nResult.Error("initial error").flatMap((x) =>\n x > 2 ? Result.Error("some error") : Result.Ok(2),\n);\n// Result.Error<"initial error">\n')),(0,a.kt)("h3",{id:"flatmaperrorf"},".flatMapError(f)"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"Result.flatMapError(f: (value: E) => Result): Result\n")),(0,a.kt)("p",null,"If the result is ",(0,a.kt)("inlineCode",{parentName:"p"},"Error(error)")," returns ",(0,a.kt)("inlineCode",{parentName:"p"},"f(error)"),", otherwise returns ",(0,a.kt)("inlineCode",{parentName:"p"},"Ok(value)"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},'Result.Error(3).flatMapError((x) =>\n x > 2 ? Result.Error("some error") : Result.Ok(2),\n);\n// Result.Error<"some error">\n\nResult.Error(1).flatMapError((x) =>\n x > 2 ? Result.Error("some error") : Result.Ok(2),\n);\n// Result.Ok<2>\n\nResult.Ok("ok").flatMapError((x) =>\n x > 2 ? Result.Error("some error") : Result.Ok(2),\n);\n// Result.Ok<"ok">\n')),(0,a.kt)("h3",{id:"getordefaultvalue"},".getOr(defaultValue)"),(0,a.kt)("blockquote",null,(0,a.kt)("p",{parentName:"blockquote"},"Alias: ",(0,a.kt)("inlineCode",{parentName:"p"},"getWithDefault"))),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"Result.getOr(defaultValue: A): A\n")),(0,a.kt)("p",null,"If the result is ",(0,a.kt)("inlineCode",{parentName:"p"},"Ok(value)")," returns ",(0,a.kt)("inlineCode",{parentName:"p"},"value"),", otherwise returns ",(0,a.kt)("inlineCode",{parentName:"p"},"defaultValue"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"Result.Ok(2).getOr(1);\n// 2\n\nResult.Error(2).getOr(1);\n// 1\n")),(0,a.kt)("h3",{id:"get"},".get()"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"Result.get(): A\n")),(0,a.kt)("p",null,"Returns the value contained in ",(0,a.kt)("inlineCode",{parentName:"p"},"Ok(value)"),". Only usable within a ",(0,a.kt)("inlineCode",{parentName:"p"},"isOk()")," check."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"const value = result.get();\n// does not compile\n\nif (result.isOk()) {\n const value = result.get();\n // value\n}\n")),(0,a.kt)("h3",{id:"geterror"},".getError()"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"Result.getError(): E\n")),(0,a.kt)("p",null,"Returns the error contained in ",(0,a.kt)("inlineCode",{parentName:"p"},"Error(error)"),". Only usable within a ",(0,a.kt)("inlineCode",{parentName:"p"},"isError()")," check."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"const error = result.getError();\n// does not compile\n\nif (result.isError()) {\n const error = result.getError();\n // error\n}\n")),(0,a.kt)("h3",{id:"isok"},".isOk()"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"Result.isOk(): boolean\n")),(0,a.kt)("p",null,"Type guard. Checks if the result is ",(0,a.kt)("inlineCode",{parentName:"p"},"Ok(value)")),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"Result.Ok(2).isOk();\n// true\n\nResult.Error(2).isOk();\n// false\n\nif (result.isOk()) {\n const value = result.get();\n}\n")),(0,a.kt)("h3",{id:"iserror"},".isError()"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"Result.isError(): boolean\n")),(0,a.kt)("p",null,"Type guard. Checks if the result is ",(0,a.kt)("inlineCode",{parentName:"p"},"Error(error)")),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"Result.Ok(2).isError();\n// false\n\nResult.Error().isError();\n// true\n\nif (result.isError()) {\n const value = result.getError();\n}\n")),(0,a.kt)("h3",{id:"tooption"},".toOption()"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"Result.toOption(): Option\n")),(0,a.kt)("p",null,"If the result is ",(0,a.kt)("inlineCode",{parentName:"p"},"Ok(value)")," returns ",(0,a.kt)("inlineCode",{parentName:"p"},"Some(value)"),", otherwise returns ",(0,a.kt)("inlineCode",{parentName:"p"},"None"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"Result.Ok(2).toOption();\n// Option.Some<2>\n\nResult.Error(2).toOption();\n// Option.None\n")),(0,a.kt)("h3",{id:"match"},".match()"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"Result.match(config: {\n Ok: (value: A) => B;\n Error: (error: E) => B;\n}): B\n")),(0,a.kt)("p",null,"Match the result state"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},'const valueToDisplay = result.match({\n Ok: (value) => value,\n Error: (error) => {\n console.error(error);\n return "fallback";\n },\n});\n')),(0,a.kt)("h3",{id:"tapfunc"},".tap(func)"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"Result.tap(func: (result: Result) => unknown): Result\n")),(0,a.kt)("p",null,"Executes ",(0,a.kt)("inlineCode",{parentName:"p"},"func")," with ",(0,a.kt)("inlineCode",{parentName:"p"},"result"),", and returns ",(0,a.kt)("inlineCode",{parentName:"p"},"result"),". Useful for logging and debugging."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"result.tap(console.log).map((x) => x * 2);\n")),(0,a.kt)("h3",{id:"tapokfunc"},".tapOk(func)"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"Result.tapOk(func: (value: A) => unknown): Result\n")),(0,a.kt)("p",null,"Executes ",(0,a.kt)("inlineCode",{parentName:"p"},"func")," with ",(0,a.kt)("inlineCode",{parentName:"p"},"ok"),", and returns ",(0,a.kt)("inlineCode",{parentName:"p"},"result"),". Useful for logging and debugging. No-op if ",(0,a.kt)("inlineCode",{parentName:"p"},"result")," is an error."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"result.tapOk(console.log).map((x) => x * 2);\n")),(0,a.kt)("h3",{id:"taperrorfunc"},".tapError(func)"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"Result.tapError(func: (error: E) => unknown): Result\n")),(0,a.kt)("p",null,"Executes ",(0,a.kt)("inlineCode",{parentName:"p"},"func")," with ",(0,a.kt)("inlineCode",{parentName:"p"},"error"),", and returns ",(0,a.kt)("inlineCode",{parentName:"p"},"result"),". Useful for logging and debugging. No-op if ",(0,a.kt)("inlineCode",{parentName:"p"},"result")," is ok."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"result.tapError(console.log).map((x) => x * 2);\n")),(0,a.kt)("h2",{id:"statics"},"Statics"),(0,a.kt)("h3",{id:"resultisresultvalue"},"Result.isResult(value)"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"isResult(value: unknown): boolean\n")),(0,a.kt)("p",null,"Type guard, checks if the provided value is a result."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"Result.isResult(Result.Ok(1));\n// true\n\nResult.isResult([]);\n// false\n")),(0,a.kt)("h3",{id:"resultallresults"},"Result.all(results)"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"all(options: Array>): Result, E>\n")),(0,a.kt)("p",null,'Turns an "array of results of value" into a "result of array of value".'),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},'Result.all([Result.Ok(1), Result.Ok(2), Result.Ok(3)]);\n// Result.Ok<[1, 2, 3]>\n\nResult.all([Result.Error("error"), Result.Ok(2), Result.Ok(3)]);\n// Result.Error<"error">\n')),(0,a.kt)("h3",{id:"resultallfromdictresults"},"Result.allFromDict(results)"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"allFromDict(options: Dict>): Result, E>\n")),(0,a.kt)("p",null,'Turns a "dict of results of value" into a "result of dict of value".'),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},'Result.allFromDict({ a: Result.Ok(1), b: Result.Ok(2), c: Result.Ok(3) });\n// Result.Ok<{a: 1, b: 2, c: 3}>\n\nResult.allFromDict({\n a: Result.Error("error"),\n b: Result.Ok(2),\n c: Result.Ok(3),\n});\n// Result.Error<"error">\n')),(0,a.kt)("h3",{id:"resultfromexecution--value"},"Result.fromExecution(() => value)"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"fromExecution(func: () => A) => Result\n")),(0,a.kt)("p",null,"Takes a function returning ",(0,a.kt)("inlineCode",{parentName:"p"},"Value")," that can throw an ",(0,a.kt)("inlineCode",{parentName:"p"},"Error")," and returns a ",(0,a.kt)("inlineCode",{parentName:"p"},"Result")),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},'Result.fromExecution(() => 1);\n// Result.Ok<1>\n\nResult.fromExecution(() => {\n throw "Something went wrong";\n});\n// Result.Error<"Something went wrong">\n')),(0,a.kt)("h3",{id:"resultfrompromisepromise"},"Result.fromPromise(promise)"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"fromPromise(promise: Promise) => Promise>\n")),(0,a.kt)("p",null,"Takes a ",(0,a.kt)("inlineCode",{parentName:"p"},"Promise")," that can fail with ",(0,a.kt)("inlineCode",{parentName:"p"},"Error")," and returns a ",(0,a.kt)("inlineCode",{parentName:"p"},"Promise>")),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"await Result.fromPromise(Promise.resolve(1));\n// Result.Ok<1>\n\nawait Result.fromPromise(Promise.reject(1));\n// Result.Error<1>\n")),(0,a.kt)("h3",{id:"resultfromoptionoption-valueifnone"},"Result.fromOption(option, valueIfNone)"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"fromOption(option: Option, valueWhenNone: E): Result\n")),(0,a.kt)("p",null,"Takes a function returning ",(0,a.kt)("inlineCode",{parentName:"p"},"Value")," that can throw an ",(0,a.kt)("inlineCode",{parentName:"p"},"Error")," and returns a ",(0,a.kt)("inlineCode",{parentName:"p"},"Result")),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},'const a = Result.fromOption(Option.Some(1), "NotFound");\n// Result.Ok<1>\n\nconst b = Result.fromOption(Option.None(), "NotFound");\n// Result.Error<"NotFound">\n')),(0,a.kt)("h2",{id:"ts-pattern-interop"},"TS Pattern interop"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'import { match, P } from "ts-pattern";\nimport { Result } from "@swan-io/boxed";\n\nmatch(myResult)\n .with(Result.P.Ok(P.select()), (value) => console.log(value))\n .with(Result.P.Error(P.select()), (error) => {\n console.error(error);\n return "fallback";\n })\n .exhaustive();\n')),(0,a.kt)("h2",{id:"cheatsheet"},"Cheatsheet"),(0,a.kt)("table",null,(0,a.kt)("thead",{parentName:"table"},(0,a.kt)("tr",{parentName:"thead"},(0,a.kt)("th",{parentName:"tr",align:null},"Method"),(0,a.kt)("th",{parentName:"tr",align:null},"Input"),(0,a.kt)("th",{parentName:"tr",align:null},"Function input"),(0,a.kt)("th",{parentName:"tr",align:null},"Function output"),(0,a.kt)("th",{parentName:"tr",align:null},"Returned value"))),(0,a.kt)("tbody",{parentName:"table"},(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("a",{parentName:"td",href:"#mapf"},(0,a.kt)("inlineCode",{parentName:"a"},"map"))),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Ok(x)")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"x")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"y")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Ok(y)"))),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("a",{parentName:"td",href:"#mapf"},(0,a.kt)("inlineCode",{parentName:"a"},"map"))),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Error(e)")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("em",{parentName:"td"},"not provided")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("em",{parentName:"td"},"not executed")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Error(e)"))),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("a",{parentName:"td",href:"#maperrorf"},(0,a.kt)("inlineCode",{parentName:"a"},"mapError"))),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Ok(x)")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("em",{parentName:"td"},"not provided")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("em",{parentName:"td"},"not executed")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Ok(x)"))),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("a",{parentName:"td",href:"#maperrorf"},(0,a.kt)("inlineCode",{parentName:"a"},"mapError"))),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Error(e)")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"e")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"f")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Error(f)"))),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("a",{parentName:"td",href:"#flatmapf"},(0,a.kt)("inlineCode",{parentName:"a"},"flatMap"))),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Ok(x)")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"x")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Ok(y)")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Ok(y)"))),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("a",{parentName:"td",href:"#flatmapf"},(0,a.kt)("inlineCode",{parentName:"a"},"flatMap"))),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Ok(x)")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"x")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Error(f)")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Error(f)"))),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("a",{parentName:"td",href:"#flatmapf"},(0,a.kt)("inlineCode",{parentName:"a"},"flatMap"))),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Error(e)")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("em",{parentName:"td"},"not provided")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("em",{parentName:"td"},"not executed")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Error(e)"))),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("a",{parentName:"td",href:"#flatmaperrorf"},(0,a.kt)("inlineCode",{parentName:"a"},"flatMapError"))),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Ok(x)")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("em",{parentName:"td"},"not provided")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("em",{parentName:"td"},"not executed")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Ok(x)"))),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("a",{parentName:"td",href:"#flatmaperrorf"},(0,a.kt)("inlineCode",{parentName:"a"},"flatMapError"))),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Error(e)")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"e")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Ok(y)")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Ok(y)"))),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("a",{parentName:"td",href:"#flatmaperrorf"},(0,a.kt)("inlineCode",{parentName:"a"},"flatMapError"))),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Error(e)")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"e")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Error(f)")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Error(f)"))))))}k.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/ace312b1.a5b8073b.js b/assets/js/ace312b1.a5b8073b.js deleted file mode 100644 index fa14862..0000000 --- a/assets/js/ace312b1.a5b8073b.js +++ /dev/null @@ -1 +0,0 @@ -"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[527],{3905:(e,t,r)=>{r.d(t,{Zo:()=>p,kt:()=>c});var n=r(7294);function a(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function l(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function o(e){for(var t=1;t=0||(a[r]=e[r]);return a}(e,t);if(Object.getOwnPropertySymbols){var l=Object.getOwnPropertySymbols(e);for(n=0;n=0||Object.prototype.propertyIsEnumerable.call(e,r)&&(a[r]=e[r])}return a}var i=n.createContext({}),u=function(e){var t=n.useContext(i),r=t;return e&&(r="function"==typeof e?e(t):o(o({},t),e)),r},p=function(e){var t=u(e.components);return n.createElement(i.Provider,{value:t},e.children)},m="mdxType",k={inlineCode:"code",wrapper:function(e){var t=e.children;return n.createElement(n.Fragment,{},t)}},d=n.forwardRef((function(e,t){var r=e.components,a=e.mdxType,l=e.originalType,i=e.parentName,p=s(e,["components","mdxType","originalType","parentName"]),m=u(r),d=a,c=m["".concat(i,".").concat(d)]||m[d]||k[d]||l;return r?n.createElement(c,o(o({ref:t},p),{},{components:r})):n.createElement(c,o({ref:t},p))}));function c(e,t){var r=arguments,a=t&&t.mdxType;if("string"==typeof e||a){var l=r.length,o=new Array(l);o[0]=d;var s={};for(var i in t)hasOwnProperty.call(t,i)&&(s[i]=t[i]);s.originalType=e,s[m]="string"==typeof e?e:a,o[1]=s;for(var u=2;u{r.r(t),r.d(t,{assets:()=>i,contentTitle:()=>o,default:()=>k,frontMatter:()=>l,metadata:()=>s,toc:()=>u});var n=r(7462),a=(r(7294),r(3905));const l={title:"Result",sidebar_label:"Result"},o=void 0,s={unversionedId:"result",id:"result",title:"Result",description:"The Result can replace exception flows.",source:"@site/docs/result.md",sourceDirName:".",slug:"/result",permalink:"/boxed/result",draft:!1,editUrl:"https://github.com/swan-io/boxed/edit/main/docs/docs/result.md",tags:[],version:"current",frontMatter:{title:"Result",sidebar_label:"Result"},sidebar:"docs",previous:{title:"Option",permalink:"/boxed/option"},next:{title:"AsyncData",permalink:"/boxed/async-data"}},i={},u=[{value:"Create a Result value",id:"create-a-result-value",level:2},{value:"Methods",id:"methods",level:2},{value:".map(f)",id:"mapf",level:3},{value:".mapError(f)",id:"maperrorf",level:3},{value:".flatMap(f)",id:"flatmapf",level:3},{value:".flatMapError(f)",id:"flatmaperrorf",level:3},{value:".getOr(defaultValue)",id:"getordefaultvalue",level:3},{value:".get()",id:"get",level:3},{value:".getError()",id:"geterror",level:3},{value:".isOk()",id:"isok",level:3},{value:".isError()",id:"iserror",level:3},{value:".toOption()",id:"tooption",level:3},{value:".match()",id:"match",level:3},{value:".tap(func)",id:"tapfunc",level:3},{value:".tapOk(func)",id:"tapokfunc",level:3},{value:".tapError(func)",id:"taperrorfunc",level:3},{value:"Statics",id:"statics",level:2},{value:"Result.isResult(value)",id:"resultisresultvalue",level:3},{value:"Result.all(results)",id:"resultallresults",level:3},{value:"Result.allFromDict(results)",id:"resultallfromdictresults",level:3},{value:"Result.fromExecution(() => value)",id:"resultfromexecution--value",level:3},{value:"Result.fromPromise(promise)",id:"resultfrompromisepromise",level:3},{value:"Result.fromOption(option, valueIfNone)",id:"resultfromoptionoption-valueifnone",level:3},{value:"TS Pattern interop",id:"ts-pattern-interop",level:2},{value:"Cheatsheet",id:"cheatsheet",level:2}],p={toc:u},m="wrapper";function k(e){let{components:t,...r}=e;return(0,a.kt)(m,(0,n.Z)({},p,r,{components:t,mdxType:"MDXLayout"}),(0,a.kt)("p",null,"The ",(0,a.kt)("inlineCode",{parentName:"p"},"Result")," can replace exception flows."),(0,a.kt)("p",null,"Exceptions can be tricky to handle: there's nothing in the type system that tracks if an error has been handled, which is error prone, and adds to your mental overhead. ",(0,a.kt)("inlineCode",{parentName:"p"},"Result")," helps as it ",(0,a.kt)("strong",{parentName:"p"},"makes the value hold the success state"),", making it dead-simple to track with a type-system."),(0,a.kt)("p",null,"Just like the ",(0,a.kt)("inlineCode",{parentName:"p"},"Option")," type, the ",(0,a.kt)("inlineCode",{parentName:"p"},"Result")," type is a box that can have two states:"),(0,a.kt)("ul",null,(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"Ok(value)")),(0,a.kt)("li",{parentName:"ul"},(0,a.kt)("inlineCode",{parentName:"li"},"Error(error)"))),(0,a.kt)("h2",{id:"create-a-result-value"},"Create a Result value"),(0,a.kt)("p",null,"To create a result, use the ",(0,a.kt)("inlineCode",{parentName:"p"},"Ok")," and ",(0,a.kt)("inlineCode",{parentName:"p"},"Error")," constructors:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'import { Result } from "@swan-io/boxed";\n\nconst ok = Result.Ok(1);\n\nconst notOk = Result.Error("something happened");\n')),(0,a.kt)("p",null,"You can convert an option to a ",(0,a.kt)("inlineCode",{parentName:"p"},"Result"),":"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'import { Result, Option } from "@swan-io/boxed";\n\nconst a = Result.fromOption(Option.Some(1), "NotFound");\n// Ok<1>\n\nconst b = Result.fromOption(Option.None(), "NotFound");\n// Error<"NotFound">\n')),(0,a.kt)("p",null,"You get interop with exceptions and promises:"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'// Let\'s say you have some function that throws an error\nconst init = (id: string) => {\n if (id.length !== 24) {\n throw new Error();\n }\n return new Client({ id });\n};\n\nconst result = Result.fromExecution(() => init(id));\n// Here, result will either be:\n// - Ok(client)\n// - Error(error)\n\n// It works with promises too:\n\nconst value = await Result.fromPromise(() => fetch("/api"));\n// `value` will either be:\n// - Ok(res)\n// - Error(error)\n')),(0,a.kt)("h2",{id:"methods"},"Methods"),(0,a.kt)("p",null,"The result type provides a few manipulation functions:"),(0,a.kt)("h3",{id:"mapf"},".map(f)"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"Result.map(f: (value: A) => B): Result\n")),(0,a.kt)("p",null,"If the result is ",(0,a.kt)("inlineCode",{parentName:"p"},"Ok(value)")," returns ",(0,a.kt)("inlineCode",{parentName:"p"},"Ok(f(value))"),", otherwise returns ",(0,a.kt)("inlineCode",{parentName:"p"},"Error(error)"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"Result.Ok(2).map((x) => x * 2);\n// Result.Ok<4>\n\nResult.Ok(2).map((x) => Result.Ok(x * 2));\n// Result.Ok>\n")),(0,a.kt)("h3",{id:"maperrorf"},".mapError(f)"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"Result.mapError(f: (value: E) => F): Result\n")),(0,a.kt)("p",null,"If the result is ",(0,a.kt)("inlineCode",{parentName:"p"},"Error(error)")," returns ",(0,a.kt)("inlineCode",{parentName:"p"},"Error(f(error))"),", otherwise returns ",(0,a.kt)("inlineCode",{parentName:"p"},"Ok(value)"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"Result.Error(2).mapError((x) => x * 2);\n// Result.Error<4>\n\nResult.Error(2).mapError((x) => Result.Ok(x * 2));\n// Result.Error>\n")),(0,a.kt)("h3",{id:"flatmapf"},".flatMap(f)"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"Result.flatMap(f: (value: A) => Result): Result\n")),(0,a.kt)("p",null,"If the result is ",(0,a.kt)("inlineCode",{parentName:"p"},"Ok(value)")," returns ",(0,a.kt)("inlineCode",{parentName:"p"},"f(value)"),", otherwise returns ",(0,a.kt)("inlineCode",{parentName:"p"},"Error(error)"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},'Result.Ok(1).flatMap((x) =>\n x > 2 ? Result.Error("some error") : Result.Ok(2),\n);\n// Result.Ok<2>\n\nResult.Ok(3).flatMap((x) =>\n x > 2 ? Result.Error("some error") : Result.Ok(2),\n);\n// Result.Error<"some error">\n\nResult.Error("initial error").flatMap((x) =>\n x > 2 ? Result.Error("some error") : Result.Ok(2),\n);\n// Result.Error<"initial error">\n')),(0,a.kt)("h3",{id:"flatmaperrorf"},".flatMapError(f)"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"Result.flatMapError(f: (value: E) => Result): Result\n")),(0,a.kt)("p",null,"If the result is ",(0,a.kt)("inlineCode",{parentName:"p"},"Error(error)")," returns ",(0,a.kt)("inlineCode",{parentName:"p"},"f(error)"),", otherwise returns ",(0,a.kt)("inlineCode",{parentName:"p"},"Ok(value)"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},'Result.Error(3).flatMapError((x) =>\n x > 2 ? Result.Error("some error") : Result.Ok(2),\n);\n// Result.Error<"some error">\n\nResult.Error(1).flatMapError((x) =>\n x > 2 ? Result.Error("some error") : Result.Ok(2),\n);\n// Result.Ok<2>\n\nResult.Ok("ok").flatMapError((x) =>\n x > 2 ? Result.Error("some error") : Result.Ok(2),\n);\n// Result.Ok<"ok">\n')),(0,a.kt)("h3",{id:"getordefaultvalue"},".getOr(defaultValue)"),(0,a.kt)("blockquote",null,(0,a.kt)("p",{parentName:"blockquote"},"Alias: ",(0,a.kt)("inlineCode",{parentName:"p"},"getWithDefault"))),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"Result.getOr(defaultValue: A): A\n")),(0,a.kt)("p",null,"If the result is ",(0,a.kt)("inlineCode",{parentName:"p"},"Ok(value)")," returns ",(0,a.kt)("inlineCode",{parentName:"p"},"value"),", otherwise returns ",(0,a.kt)("inlineCode",{parentName:"p"},"defaultValue"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"Result.Ok(2).getOr(1);\n// 2\n\nResult.Error(2).getOr(1);\n// 1\n")),(0,a.kt)("h3",{id:"get"},".get()"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"Result.get(): A\n")),(0,a.kt)("p",null,"Returns the value contained in ",(0,a.kt)("inlineCode",{parentName:"p"},"Ok(value)"),". Only usable within a ",(0,a.kt)("inlineCode",{parentName:"p"},"isOk()")," check."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"const value = result.get();\n// does not compile\n\nif (result.isOk()) {\n const value = result.get();\n // value\n}\n")),(0,a.kt)("h3",{id:"geterror"},".getError()"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"Result.getError(): E\n")),(0,a.kt)("p",null,"Returns the error contained in ",(0,a.kt)("inlineCode",{parentName:"p"},"Error(error)"),". Only usable within a ",(0,a.kt)("inlineCode",{parentName:"p"},"isError()")," check."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"const error = result.getError();\n// does not compile\n\nif (result.isError()) {\n const error = result.getError();\n // error\n}\n")),(0,a.kt)("h3",{id:"isok"},".isOk()"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"Result.isOk(): boolean\n")),(0,a.kt)("p",null,"Type guard. Checks if the result is ",(0,a.kt)("inlineCode",{parentName:"p"},"Ok(value)")),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"Result.Ok(2).isOk();\n// true\n\nResult.Error(2).isOk();\n// false\n\nif (result.isOk()) {\n const value = result.get();\n}\n")),(0,a.kt)("h3",{id:"iserror"},".isError()"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"Result.isError(): boolean\n")),(0,a.kt)("p",null,"Type guard. Checks if the result is ",(0,a.kt)("inlineCode",{parentName:"p"},"Error(error)")),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"Result.Ok(2).isError();\n// false\n\nResult.Error().isError();\n// true\n\nif (result.isError()) {\n const value = result.getError();\n}\n")),(0,a.kt)("h3",{id:"tooption"},".toOption()"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"Result.toOption(): Option\n")),(0,a.kt)("p",null,"If the result is ",(0,a.kt)("inlineCode",{parentName:"p"},"Ok(value)")," returns ",(0,a.kt)("inlineCode",{parentName:"p"},"Some(value)"),", otherwise returns ",(0,a.kt)("inlineCode",{parentName:"p"},"None"),"."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"Result.Ok(2).toOption();\n// Option.Some<2>\n\nResult.Error(2).toOption();\n// Option.None\n")),(0,a.kt)("h3",{id:"match"},".match()"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"Result.match(config: {\n Ok: (value: A) => B;\n Error: (error: E) => B;\n}): B\n")),(0,a.kt)("p",null,"Match the result state"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},'const valueToDisplay = result.match({\n Ok: (value) => value,\n Error: (error) => {\n console.error(error);\n return "fallback";\n },\n});\n')),(0,a.kt)("h3",{id:"tapfunc"},".tap(func)"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"Result.tap(func: (result: Result) => unknown): Result\n")),(0,a.kt)("p",null,"Executes ",(0,a.kt)("inlineCode",{parentName:"p"},"func")," with ",(0,a.kt)("inlineCode",{parentName:"p"},"result"),", and returns ",(0,a.kt)("inlineCode",{parentName:"p"},"result"),". Useful for logging and debugging."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"result.tap(console.log).map((x) => x * 2);\n")),(0,a.kt)("h3",{id:"tapokfunc"},".tapOk(func)"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"Result.tapOk(func: (value: A) => unknown): Result\n")),(0,a.kt)("p",null,"Executes ",(0,a.kt)("inlineCode",{parentName:"p"},"func")," with ",(0,a.kt)("inlineCode",{parentName:"p"},"ok"),", and returns ",(0,a.kt)("inlineCode",{parentName:"p"},"result"),". Useful for logging and debugging. No-op if ",(0,a.kt)("inlineCode",{parentName:"p"},"result")," is an error."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"result.tapOk(console.log).map((x) => x * 2);\n")),(0,a.kt)("h3",{id:"taperrorfunc"},".tapError(func)"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"Result.tapError(func: (error: E) => unknown): Result\n")),(0,a.kt)("p",null,"Executes ",(0,a.kt)("inlineCode",{parentName:"p"},"func")," with ",(0,a.kt)("inlineCode",{parentName:"p"},"error"),", and returns ",(0,a.kt)("inlineCode",{parentName:"p"},"result"),". Useful for logging and debugging. No-op if ",(0,a.kt)("inlineCode",{parentName:"p"},"result")," is ok."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"result.tapError(console.log).map((x) => x * 2);\n")),(0,a.kt)("h2",{id:"statics"},"Statics"),(0,a.kt)("h3",{id:"resultisresultvalue"},"Result.isResult(value)"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"isResult(value: unknown): boolean\n")),(0,a.kt)("p",null,"Type guard, checks if the provided value is a result."),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"Result.isResult(Result.Ok(1));\n// true\n\nResult.isResult([]);\n// false\n")),(0,a.kt)("h3",{id:"resultallresults"},"Result.all(results)"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"all(options: Array>): Result, E>\n")),(0,a.kt)("p",null,'Turns an "array of results of value" into a "result of array of value".'),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},'Result.all([Result.Ok(1), Result.Ok(2), Result.Ok(3)]);\n// Result.Ok<[1, 2, 3]>\n\nResult.all([Result.Error("error"), Result.Ok(2), Result.Ok(3)]);\n// Result.Error<"error">\n')),(0,a.kt)("h3",{id:"resultallfromdictresults"},"Result.allFromDict(results)"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"allFromDict(options: Dict>): Result, E>\n")),(0,a.kt)("p",null,'Turns a "dict of results of value" into a "result of dict of value".'),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},'Result.allFromDict({ a: Result.Ok(1), b: Result.Ok(2), c: Result.Ok(3) });\n// Result.Ok<{a: 1, b: 2, c: 3}>\n\nResult.allFromDict({\n a: Result.Error("error"),\n b: Result.Ok(2),\n c: Result.Ok(3),\n});\n// Result.Error<"error">\n')),(0,a.kt)("h3",{id:"resultfromexecution--value"},"Result.fromExecution(() => value)"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"fromExecution(func: () => A) => Result\n")),(0,a.kt)("p",null,"Takes a function returning ",(0,a.kt)("inlineCode",{parentName:"p"},"Value")," that can throw an ",(0,a.kt)("inlineCode",{parentName:"p"},"Error")," and returns a ",(0,a.kt)("inlineCode",{parentName:"p"},"Result")),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},'Result.fromExecution(() => 1);\n// Result.Ok<1>\n\nResult.fromExecution(() => {\n throw "Something went wrong";\n});\n// Result.Error<"Something went wrong">\n')),(0,a.kt)("h3",{id:"resultfrompromisepromise"},"Result.fromPromise(promise)"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"fromPromise(promise: Promise) => Promise>\n")),(0,a.kt)("p",null,"Takes a ",(0,a.kt)("inlineCode",{parentName:"p"},"Promise")," that can fail with ",(0,a.kt)("inlineCode",{parentName:"p"},"Error")," and returns a ",(0,a.kt)("inlineCode",{parentName:"p"},"Promise>")),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},"await Result.fromPromise(Promise.resolve(1));\n// Result.Ok<1>\n\nawait Result.fromPromise(Promise.reject(1));\n// Result.Error<1>\n")),(0,a.kt)("h3",{id:"resultfromoptionoption-valueifnone"},"Result.fromOption(option, valueIfNone)"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},"fromOption(option: Option, valueWhenNone: E): Result\n")),(0,a.kt)("p",null,"Takes a function returning ",(0,a.kt)("inlineCode",{parentName:"p"},"Value")," that can throw an ",(0,a.kt)("inlineCode",{parentName:"p"},"Error")," and returns a ",(0,a.kt)("inlineCode",{parentName:"p"},"Result")),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts",metastring:'title="Examples"',title:'"Examples"'},'const a = Result.fromOption(Option.Some(1), "NotFound");\n// Result.Ok<1>\n\nconst b = Result.fromOption(Option.None(), "NotFound");\n// Result.Error<"NotFound">\n')),(0,a.kt)("h2",{id:"ts-pattern-interop"},"TS Pattern interop"),(0,a.kt)("pre",null,(0,a.kt)("code",{parentName:"pre",className:"language-ts"},'import { match, P } from "ts-pattern";\nimport { Result } from "@swan-io/boxed";\n\nmatch(myResult)\n .with(Result.P.Ok(P.select()), (value) => console.log(value))\n .with(Result.P.Error(P.select()), (error) => {\n console.error(error);\n return "fallback";\n })\n .exhaustive();\n')),(0,a.kt)("h2",{id:"cheatsheet"},"Cheatsheet"),(0,a.kt)("table",null,(0,a.kt)("thead",{parentName:"table"},(0,a.kt)("tr",{parentName:"thead"},(0,a.kt)("th",{parentName:"tr",align:null},"Method"),(0,a.kt)("th",{parentName:"tr",align:null},"Input"),(0,a.kt)("th",{parentName:"tr",align:null},"Function input"),(0,a.kt)("th",{parentName:"tr",align:null},"Function output"),(0,a.kt)("th",{parentName:"tr",align:null},"Returned value"))),(0,a.kt)("tbody",{parentName:"table"},(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("a",{parentName:"td",href:"#mapf"},(0,a.kt)("inlineCode",{parentName:"a"},"map"))),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Ok(x)")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"x")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"y")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Ok(y)"))),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("a",{parentName:"td",href:"#mapf"},(0,a.kt)("inlineCode",{parentName:"a"},"map"))),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Error(e)")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("em",{parentName:"td"},"not provided")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("em",{parentName:"td"},"not executed")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Error(e)"))),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("a",{parentName:"td",href:"#maperrorf"},(0,a.kt)("inlineCode",{parentName:"a"},"mapError"))),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Ok(x)")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("em",{parentName:"td"},"not provided")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("em",{parentName:"td"},"not executed")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Ok(x)"))),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("a",{parentName:"td",href:"#maperrorf"},(0,a.kt)("inlineCode",{parentName:"a"},"mapError"))),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Error(e)")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"e")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"f")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Error(f)"))),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("a",{parentName:"td",href:"#flatmapf"},(0,a.kt)("inlineCode",{parentName:"a"},"flatMap"))),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Ok(x)")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"x")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Ok(y)")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Ok(y)"))),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("a",{parentName:"td",href:"#flatmapf"},(0,a.kt)("inlineCode",{parentName:"a"},"flatMap"))),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Ok(x)")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"x")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Error(f)")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Error(f)"))),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("a",{parentName:"td",href:"#flatmapf"},(0,a.kt)("inlineCode",{parentName:"a"},"flatMap"))),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Error(e)")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("em",{parentName:"td"},"not provided")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("em",{parentName:"td"},"not executed")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Error(e)"))),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("a",{parentName:"td",href:"#flatmaperrorf"},(0,a.kt)("inlineCode",{parentName:"a"},"flatMapError"))),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Ok(x)")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("em",{parentName:"td"},"not provided")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("em",{parentName:"td"},"not executed")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Ok(x)"))),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("a",{parentName:"td",href:"#flatmaperrorf"},(0,a.kt)("inlineCode",{parentName:"a"},"flatMapError"))),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Error(e)")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"e")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Ok(y)")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Ok(y)"))),(0,a.kt)("tr",{parentName:"tbody"},(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("a",{parentName:"td",href:"#flatmaperrorf"},(0,a.kt)("inlineCode",{parentName:"a"},"flatMapError"))),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Error(e)")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"e")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Error(f)")),(0,a.kt)("td",{parentName:"tr",align:null},(0,a.kt)("inlineCode",{parentName:"td"},"Error(f)"))))))}k.isMDXComponent=!0}}]); \ No newline at end of file diff --git a/assets/js/c4f5d8e4.46cfd268.js b/assets/js/c4f5d8e4.f6e0632f.js similarity index 99% rename from assets/js/c4f5d8e4.46cfd268.js rename to assets/js/c4f5d8e4.f6e0632f.js index 5729ea5..b7c35f4 100644 --- a/assets/js/c4f5d8e4.46cfd268.js +++ b/assets/js/c4f5d8e4.f6e0632f.js @@ -1 +1 @@ -"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[195],{3261:(e,t,n)=>{n.r(t),n.d(t,{default:()=>v});var l=n(9960),a=n(2263),r=n(7462),o=n(6010),s=n(7294);const c={features:"features_t9lD",feature:"feature_CLW0",svgContainer:"svgContainer_uQs8",svg:"svg_FQEk"},i=[{title:"Avoid accidental complexity",svg:s.createElement("svg",{className:c.svg,width:"24",height:"24",fill:"none",viewBox:"0 0 24 24",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"m18.492 2.33 3.179 3.179a2.25 2.25 0 0 1 0 3.182l-2.584 2.584A2.25 2.25 0 0 1 21 13.5v5.25A2.25 2.25 0 0 1 18.75 21H5.25A2.25 2.25 0 0 1 3 18.75V5.25A2.25 2.25 0 0 1 5.25 3h5.25a2.25 2.25 0 0 1 2.225 1.915L15.31 2.33a2.25 2.25 0 0 1 3.182 0ZM4.5 18.75c0 .414.336.75.75.75l5.999-.001.001-6.75H4.5v6Zm8.249.749h6.001a.75.75 0 0 0 .75-.75V13.5a.75.75 0 0 0-.75-.75h-6.001v6.75Zm-2.249-15H5.25a.75.75 0 0 0-.75.75v6h6.75v-6a.75.75 0 0 0-.75-.75Zm2.25 4.81v1.94h1.94l-1.94-1.94Zm3.62-5.918-3.178 3.178a.75.75 0 0 0 0 1.061l3.179 3.179a.75.75 0 0 0 1.06 0l3.18-3.179a.75.75 0 0 0 0-1.06l-3.18-3.18a.75.75 0 0 0-1.06 0Z",fill:"#6240B5"})),description:s.createElement(s.Fragment,null,"Boxed provides functional building blocks that make your code more"," ",s.createElement("strong",null,"maintainable"),", more ",s.createElement("strong",null,"expressive"),", and"," ",s.createElement("strong",null,"safer"),".")},{title:"Focused on DX",svg:s.createElement("svg",{className:c.svg,width:"24",height:"24",fill:"none",viewBox:"0 0 24 24",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M2 5.25A3.25 3.25 0 0 1 5.25 2h11.5A3.25 3.25 0 0 1 20 5.25v2.76a4.508 4.508 0 0 0-1.5.096V7h-15v9.75c0 .966.784 1.75 1.75 1.75h7.985l-.441.764a2.457 2.457 0 0 0-.28.736H5.25A3.25 3.25 0 0 1 2 16.75V5.25ZM5.25 3.5A1.75 1.75 0 0 0 3.5 5.25v.25h15v-.25a1.75 1.75 0 0 0-1.75-1.75H5.25ZM19.857 9a3.496 3.496 0 0 0-3.356 1.736 3.5 3.5 0 0 0 .184 3.788l-3.025 5.24a1.459 1.459 0 0 0 2.526 1.458l3.03-5.25a3.5 3.5 0 0 0 2.976-5.761l-1.65 2.858a1.167 1.167 0 1 1-2.021-1.167l1.65-2.858A3.478 3.478 0 0 0 19.857 9Zm-9.554.243a.75.75 0 0 1-.046 1.06L7.86 12.5l2.397 2.197a.75.75 0 0 1-1.014 1.106l-3-2.75a.75.75 0 0 1 0-1.106l3-2.75a.75.75 0 0 1 1.06.046Zm2.954 6.56 2.02-1.852a4.495 4.495 0 0 1-.008-2.91l-2.012-1.844a.75.75 0 0 0-1.014 1.106L14.64 12.5l-2.397 2.197a.75.75 0 0 0 1.014 1.106Z",fill:"#6240B5"})),description:s.createElement(s.Fragment,null,"We provide a very ",s.createElement("strong",null,"small API surface"),". With easy interop, and ",s.createElement("strong",null,"compatibility")," with the ecosystem (like"," ",s.createElement("a",{href:"https://github.com/gvergnaud/ts-pattern"},"ts-pattern"),")")},{title:"Easy to reason about",svg:s.createElement("svg",{className:c.svg,width:"24",height:"24",fill:"none",viewBox:"0 0 24 24",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M4 18a2 2 0 1 1 0 4 2 2 0 0 1 0-4Zm0 1.5a.5.5 0 1 0 0 1 .5.5 0 0 0 0-1ZM9.5 15a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5Zm0 1.5a1 1 0 1 0 0 2 1 1 0 0 0 0-2ZM11.823 2a5.414 5.414 0 0 1 5.33 4.47h.082a3.765 3.765 0 1 1 0 7.53H6.412a3.765 3.765 0 1 1 0-7.53h.081A5.414 5.414 0 0 1 11.823 2Zm.006 1.498a3.927 3.927 0 0 0-3.923 3.728.693.693 0 0 1-.692.659h-.7a2.31 2.31 0 1 0 0 4.617h10.63a2.31 2.31 0 1 0 0-4.617h-.7a.693.693 0 0 1-.692-.659 3.927 3.927 0 0 0-3.923-3.728Z",fill:"#6240B5"})),description:s.createElement(s.Fragment,null,"The concepts exposed by Boxed are ",s.createElement("strong",null,"simple")," and"," ",s.createElement("strong",null,"accessible"),": you don't need a CS\xa0degree to get started.")}];function m(e){let{svg:t,title:n,description:l}=e;return s.createElement("div",{className:(0,o.Z)("col col--4",c.feature)},s.createElement("div",{className:c.svgContainer},t),s.createElement("div",{className:"text--center padding-horiz--md"},s.createElement("h3",null,n),s.createElement("p",null,l)))}function u(){return s.createElement("section",{className:c.features},s.createElement("div",{className:"container"},s.createElement("div",{className:"row"},i.map(((e,t)=>s.createElement(m,(0,r.Z)({key:t},e)))))))}var d=n(814),h=n(7452);const g={heroBanner:"heroBanner_qdFl",heroLogo:"heroLogo_U6bI",heroLeft:"heroLeft_IUfu",heroTitle:"heroTitle_qg2I",heroSubtitle:"heroSubtitle_jFu1",heroButtons:"heroButtons_r52D",heroButton:"heroButton_GTT_",heroButtonSeparator:"heroButtonSeparator_jW1u",heroCode:"heroCode_u6tt",heroCodeBlock:"heroCodeBlock_lMWU",contentBlock:"contentBlock_lLKz",contentBlockReversed:"contentBlockReversed_xXjZ",contentBlockSide:"contentBlockSide_gcjd"};function E(){const{siteConfig:e}=(0,a.Z)();return s.createElement("header",{className:(0,o.Z)("hero hero--primary",g.heroBanner)},s.createElement("div",{className:g.heroLeft},s.createElement("img",{src:"./img/logo.svg",alt:"Boxed logo",className:g.heroLogo}),s.createElement("div",null,s.createElement("h1",{className:g.heroTitle},e.title),s.createElement("p",{className:g.heroSubtitle},e.tagline),s.createElement("div",{className:g.heroButtons},s.createElement(l.Z,{className:(0,o.Z)("button button--lg",g.heroButton),to:"/getting-started"},"Get started"),s.createElement("span",{className:g.heroButtonSeparator}),s.createElement(l.Z,{className:(0,o.Z)("button button--lg",g.heroButton),to:"/option"},"API reference")))),s.createElement("div",{className:g.heroCode},s.createElement(d.Z,{className:g.heroCodeBlock,language:"typescript"},'import { AsyncData } from "@swan-io/boxed";\n\nconst UserCard = ({user}: {user: AsyncData}) => {\n return user.match({\n NotAsked: () => null,\n Loading: () => `Loading`,\n Done: (user) => {\n const name = user.name.getOr("anonymous");\n return `Hello ${name}!`;\n },\n });\n};')))}const p=e=>{let{children:t,reversed:n=!1,title:l,description:a}=e;return s.createElement("div",{className:n?g.contentBlockReversed:g.contentBlock},s.createElement("div",{className:g.contentBlockSide},t),s.createElement("div",{className:g.contentBlockSide},s.createElement("h2",null,l),s.createElement("p",null,a)))};function v(){const{siteConfig:e}=(0,a.Z)(),t=s.useRef(null);return s.useEffect((()=>{const e=t.current;if(e){const t=new IntersectionObserver((t=>{t.forEach((t=>{if(t.isIntersecting){const t=e.querySelector("video");t&&t.play()}}))}),{rootMargin:"100px"});return t.observe(e),()=>t.unobserve(e)}}),[]),s.createElement(h.Z,{title:`Boxed: ${e.tagline}`,description:"Essential building-blocks for functional & safe TypeScript code"},s.createElement(E,null),s.createElement("main",null,s.createElement(u,null),s.createElement("div",{className:"container"},s.createElement(p,{children:s.createElement("img",{style:{width:"100%",borderRadius:"1rem"},loading:"lazy",src:"/boxed/img/react.jpg",alt:"Example of a request lifecycle management in a React component using Boxed.AsyncData"}),title:"Build with the right tools",description:s.createElement(s.Fragment,null,"By using ",s.createElement("strong",null,"functional type-safe constructs")," like"," ",s.createElement("a",{href:"./option"},"Option"),", ",s.createElement("a",{href:"./result"},"Result")," and"," ",s.createElement("a",{href:"./async-data"},"AsyncData"),", you can"," ",s.createElement("strong",null,"eliminate bugs right from the modeling"),".",s.createElement("br",null),s.createElement("br",null),"Your code will be ",s.createElement("strong",null,"simpler"),","," ",s.createElement("strong",null,"safer"),",and"," ",s.createElement("strong",null,"easier to reason about")," than with regular null-checks, exception flows and manual value tracking.",s.createElement("br",null),s.createElement("br",null),"All of that for ",s.createElement("strong",null,"less that 3KBs when gzipped"),"!")}),s.createElement(p,{reversed:!0,title:"Tailored for your IDE",description:s.createElement(s.Fragment,null,"Thanks to our ",s.createElement("strong",null,"chaining API"),", you get a nice"," ",s.createElement("strong",null,"autocomplete")," right from the value and can easily name intermediate variables.",s.createElement("br",null),s.createElement("br",null),"Boxed leverages the JavaScript class API so that you don't need to import any module to work with a Boxed value:"," ",s.createElement("strong",null,"it's all available as a method"),". On top of that, the Boxed API is minimal, so that your tooling doesn't feel overwhelming."),children:s.createElement("div",{style:{paddingBottom:"70.26737967914438%",position:"relative"},ref:t},s.createElement("video",{style:{position:"absolute",top:0,left:0,width:"100%",borderRadius:"1rem"},muted:!0,loop:!0,src:"/boxed/video/ide.mov",playsInline:!0,controls:!1,preload:"metadata"}))}),s.createElement(p,{title:"Get productive immediately",description:s.createElement(s.Fragment,null,"Boxed gives you the tools you need without requiring loads of theoretical knowledge.",s.createElement("br",null),s.createElement("br",null),"We provide ",s.createElement("strong",null,"simple naming"),","," ",s.createElement("strong",null,"documentation")," and"," ",s.createElement("strong",null,"escape hatches")," so that you don't get stuck. You get to ",s.createElement("strong",null,"learn as you use the library")," instead of getting frustrated over complex concepts."),children:s.createElement("img",{style:{width:"100%",borderRadius:"1rem"},loading:"lazy",src:"/boxed/img/cheatsheet.jpg",alt:"Cheatsheet table for the types of the map and flatMap functions"})}))))}}}]); \ No newline at end of file +"use strict";(self.webpackChunkdocs=self.webpackChunkdocs||[]).push([[195],{3261:(e,t,n)=>{n.r(t),n.d(t,{default:()=>v});var l=n(9960),a=n(2263),r=n(7462),o=n(6010),s=n(7294);const c={features:"features_t9lD",feature:"feature_CLW0",svgContainer:"svgContainer_uQs8",svg:"svg_FQEk"},i=[{title:"Avoid accidental complexity",svg:s.createElement("svg",{className:c.svg,width:"24",height:"24",fill:"none",viewBox:"0 0 24 24",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"m18.492 2.33 3.179 3.179a2.25 2.25 0 0 1 0 3.182l-2.584 2.584A2.25 2.25 0 0 1 21 13.5v5.25A2.25 2.25 0 0 1 18.75 21H5.25A2.25 2.25 0 0 1 3 18.75V5.25A2.25 2.25 0 0 1 5.25 3h5.25a2.25 2.25 0 0 1 2.225 1.915L15.31 2.33a2.25 2.25 0 0 1 3.182 0ZM4.5 18.75c0 .414.336.75.75.75l5.999-.001.001-6.75H4.5v6Zm8.249.749h6.001a.75.75 0 0 0 .75-.75V13.5a.75.75 0 0 0-.75-.75h-6.001v6.75Zm-2.249-15H5.25a.75.75 0 0 0-.75.75v6h6.75v-6a.75.75 0 0 0-.75-.75Zm2.25 4.81v1.94h1.94l-1.94-1.94Zm3.62-5.918-3.178 3.178a.75.75 0 0 0 0 1.061l3.179 3.179a.75.75 0 0 0 1.06 0l3.18-3.179a.75.75 0 0 0 0-1.06l-3.18-3.18a.75.75 0 0 0-1.06 0Z",fill:"#6240B5"})),description:s.createElement(s.Fragment,null,"Boxed provides functional building blocks that make your code more"," ",s.createElement("strong",null,"maintainable"),", more ",s.createElement("strong",null,"expressive"),", and"," ",s.createElement("strong",null,"safer"),".")},{title:"Focused on DX",svg:s.createElement("svg",{className:c.svg,width:"24",height:"24",fill:"none",viewBox:"0 0 24 24",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M2 5.25A3.25 3.25 0 0 1 5.25 2h11.5A3.25 3.25 0 0 1 20 5.25v2.76a4.508 4.508 0 0 0-1.5.096V7h-15v9.75c0 .966.784 1.75 1.75 1.75h7.985l-.441.764a2.457 2.457 0 0 0-.28.736H5.25A3.25 3.25 0 0 1 2 16.75V5.25ZM5.25 3.5A1.75 1.75 0 0 0 3.5 5.25v.25h15v-.25a1.75 1.75 0 0 0-1.75-1.75H5.25ZM19.857 9a3.496 3.496 0 0 0-3.356 1.736 3.5 3.5 0 0 0 .184 3.788l-3.025 5.24a1.459 1.459 0 0 0 2.526 1.458l3.03-5.25a3.5 3.5 0 0 0 2.976-5.761l-1.65 2.858a1.167 1.167 0 1 1-2.021-1.167l1.65-2.858A3.478 3.478 0 0 0 19.857 9Zm-9.554.243a.75.75 0 0 1-.046 1.06L7.86 12.5l2.397 2.197a.75.75 0 0 1-1.014 1.106l-3-2.75a.75.75 0 0 1 0-1.106l3-2.75a.75.75 0 0 1 1.06.046Zm2.954 6.56 2.02-1.852a4.495 4.495 0 0 1-.008-2.91l-2.012-1.844a.75.75 0 0 0-1.014 1.106L14.64 12.5l-2.397 2.197a.75.75 0 0 0 1.014 1.106Z",fill:"#6240B5"})),description:s.createElement(s.Fragment,null,"We provide a very ",s.createElement("strong",null,"small API surface"),". With easy interop, and ",s.createElement("strong",null,"compatibility")," with the ecosystem (like"," ",s.createElement("a",{href:"https://github.com/gvergnaud/ts-pattern"},"ts-pattern"),")")},{title:"Easy to reason about",svg:s.createElement("svg",{className:c.svg,width:"24",height:"24",fill:"none",viewBox:"0 0 24 24",xmlns:"http://www.w3.org/2000/svg"},s.createElement("path",{d:"M4 18a2 2 0 1 1 0 4 2 2 0 0 1 0-4Zm0 1.5a.5.5 0 1 0 0 1 .5.5 0 0 0 0-1ZM9.5 15a2.5 2.5 0 1 1 0 5 2.5 2.5 0 0 1 0-5Zm0 1.5a1 1 0 1 0 0 2 1 1 0 0 0 0-2ZM11.823 2a5.414 5.414 0 0 1 5.33 4.47h.082a3.765 3.765 0 1 1 0 7.53H6.412a3.765 3.765 0 1 1 0-7.53h.081A5.414 5.414 0 0 1 11.823 2Zm.006 1.498a3.927 3.927 0 0 0-3.923 3.728.693.693 0 0 1-.692.659h-.7a2.31 2.31 0 1 0 0 4.617h10.63a2.31 2.31 0 1 0 0-4.617h-.7a.693.693 0 0 1-.692-.659 3.927 3.927 0 0 0-3.923-3.728Z",fill:"#6240B5"})),description:s.createElement(s.Fragment,null,"The concepts exposed by Boxed are ",s.createElement("strong",null,"simple")," and"," ",s.createElement("strong",null,"accessible"),": you don't need a CS\xa0degree to get started.")}];function m(e){let{svg:t,title:n,description:l}=e;return s.createElement("div",{className:(0,o.Z)("col col--4",c.feature)},s.createElement("div",{className:c.svgContainer},t),s.createElement("div",{className:"text--center padding-horiz--md"},s.createElement("h3",null,n),s.createElement("p",null,l)))}function u(){return s.createElement("section",{className:c.features},s.createElement("div",{className:"container"},s.createElement("div",{className:"row"},i.map(((e,t)=>s.createElement(m,(0,r.Z)({key:t},e)))))))}var d=n(814),h=n(7452);const g={heroBanner:"heroBanner_qdFl",heroLogo:"heroLogo_U6bI",heroLeft:"heroLeft_IUfu",heroTitle:"heroTitle_qg2I",heroSubtitle:"heroSubtitle_jFu1",heroButtons:"heroButtons_r52D",heroButton:"heroButton_GTT_",heroButtonSeparator:"heroButtonSeparator_jW1u",heroCode:"heroCode_u6tt",heroCodeBlock:"heroCodeBlock_lMWU",contentBlock:"contentBlock_lLKz",contentBlockReversed:"contentBlockReversed_xXjZ",contentBlockSide:"contentBlockSide_gcjd"};function E(){const{siteConfig:e}=(0,a.Z)();return s.createElement("header",{className:(0,o.Z)("hero hero--primary",g.heroBanner)},s.createElement("div",{className:g.heroLeft},s.createElement("img",{src:"./img/logo.svg",alt:"Boxed logo",className:g.heroLogo}),s.createElement("div",null,s.createElement("h1",{className:g.heroTitle},e.title),s.createElement("p",{className:g.heroSubtitle},e.tagline),s.createElement("div",{className:g.heroButtons},s.createElement(l.Z,{className:(0,o.Z)("button button--lg",g.heroButton),to:"/getting-started"},"Get started"),s.createElement("span",{className:g.heroButtonSeparator}),s.createElement(l.Z,{className:(0,o.Z)("button button--lg",g.heroButton),to:"/option"},"API reference")))),s.createElement("div",{className:g.heroCode},s.createElement(d.Z,{className:g.heroCodeBlock,language:"typescript"},'import { AsyncData } from "@swan-io/boxed";\n\nconst UserCard = ({user}: {user: AsyncData}) => {\n return user.match({\n NotAsked: () => null,\n Loading: () => `Loading`,\n Done: (user) => {\n const name = user.name.getOr("anonymous");\n return `Hello ${name}!`;\n },\n });\n};')))}const p=e=>{let{children:t,reversed:n=!1,title:l,description:a}=e;return s.createElement("div",{className:n?g.contentBlockReversed:g.contentBlock},s.createElement("div",{className:g.contentBlockSide},t),s.createElement("div",{className:g.contentBlockSide},s.createElement("h2",null,l),s.createElement("p",null,a)))};function v(){const{siteConfig:e}=(0,a.Z)(),t=s.useRef(null);return s.useEffect((()=>{const e=t.current;if(e){const t=new IntersectionObserver((t=>{t.forEach((t=>{if(t.isIntersecting){const t=e.querySelector("video");t&&t.play()}}))}),{rootMargin:"100px"});return t.observe(e),()=>t.unobserve(e)}}),[]),s.createElement(h.Z,{title:`Boxed: ${e.tagline}`,description:"Essential building-blocks for functional & safe TypeScript code"},s.createElement(E,null),s.createElement("main",null,s.createElement(u,null),s.createElement("div",{className:"container"},s.createElement(p,{children:s.createElement("img",{style:{width:"100%",borderRadius:"1rem"},loading:"lazy",src:"/boxed/img/react.jpg",alt:"Example of a request lifecycle management in a React component using Boxed.AsyncData"}),title:"Build with the right tools",description:s.createElement(s.Fragment,null,"By using ",s.createElement("strong",null,"functional type-safe constructs")," like"," ",s.createElement("a",{href:"./option"},"Option"),", ",s.createElement("a",{href:"./result"},"Result")," and"," ",s.createElement("a",{href:"./async-data"},"AsyncData"),", you can"," ",s.createElement("strong",null,"eliminate bugs right from the modeling"),".",s.createElement("br",null),s.createElement("br",null),"Your code will be ",s.createElement("strong",null,"simpler"),","," ",s.createElement("strong",null,"safer"),",and"," ",s.createElement("strong",null,"easier to reason about")," than with regular null-checks, exception flows and manual value tracking.",s.createElement("br",null),s.createElement("br",null),"All of that for ",s.createElement("strong",null,"less that 3KBs when gzipped"),"!")}),s.createElement(p,{reversed:!0,title:"Tailored for your IDE",description:s.createElement(s.Fragment,null,"Thanks to our ",s.createElement("strong",null,"chaining API"),", you get a nice"," ",s.createElement("strong",null,"autocomplete")," right from the value and can easily name intermediate variables.",s.createElement("br",null),s.createElement("br",null),"Boxed leverages the JavaScript class API so that you don't need to import any module to work with a Boxed value:"," ",s.createElement("strong",null,"it's all available as a method"),". On top of that, the Boxed API is minimal, so that your tooling doesn't feel overwhelming."),children:s.createElement("div",{style:{paddingBottom:"70.26737967914438%",position:"relative"},ref:t},s.createElement("video",{style:{position:"absolute",top:0,left:0,width:"100%",borderRadius:"1rem"},muted:!0,loop:!0,src:"/boxed/video/ide.m4v",playsInline:!0,controls:!1,preload:"metadata"}))}),s.createElement(p,{title:"Get productive immediately",description:s.createElement(s.Fragment,null,"Boxed gives you the tools you need without requiring loads of theoretical knowledge.",s.createElement("br",null),s.createElement("br",null),"We provide ",s.createElement("strong",null,"simple naming"),","," ",s.createElement("strong",null,"documentation")," and"," ",s.createElement("strong",null,"escape hatches")," so that you don't get stuck. You get to ",s.createElement("strong",null,"learn as you use the library")," instead of getting frustrated over complex concepts."),children:s.createElement("img",{style:{width:"100%",borderRadius:"1rem"},loading:"lazy",src:"/boxed/img/cheatsheet.jpg",alt:"Cheatsheet table for the types of the map and flatMap functions"})}))))}}}]); \ No newline at end of file diff --git a/assets/js/runtime~main.8d6b665c.js b/assets/js/runtime~main.c701a566.js similarity index 94% rename from assets/js/runtime~main.8d6b665c.js rename to assets/js/runtime~main.c701a566.js index 293950a..ca176d9 100644 --- a/assets/js/runtime~main.8d6b665c.js +++ b/assets/js/runtime~main.c701a566.js @@ -1 +1 @@ -(()=>{"use strict";var e,t,r,a,o,d={},f={};function n(e){var t=f[e];if(void 0!==t)return t.exports;var r=f[e]={id:e,loaded:!1,exports:{}};return d[e].call(r.exports,r,r.exports,n),r.loaded=!0,r.exports}n.m=d,n.c=f,e=[],n.O=(t,r,a,o)=>{if(!r){var d=1/0;for(i=0;i=o)&&Object.keys(n.O).every((e=>n.O[e](r[c])))?r.splice(c--,1):(f=!1,o0&&e[i-1][2]>o;i--)e[i]=e[i-1];e[i]=[r,a,o]},n.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return n.d(t,{a:t}),t},r=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,n.t=function(e,a){if(1&a&&(e=this(e)),8&a)return e;if("object"==typeof e&&e){if(4&a&&e.__esModule)return e;if(16&a&&"function"==typeof e.then)return e}var o=Object.create(null);n.r(o);var d={};t=t||[null,r({}),r([]),r(r)];for(var f=2&a&&e;"object"==typeof f&&!~t.indexOf(f);f=r(f))Object.getOwnPropertyNames(f).forEach((t=>d[t]=()=>e[t]));return d.default=()=>e,n.d(o,d),o},n.d=(e,t)=>{for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},n.f={},n.e=e=>Promise.all(Object.keys(n.f).reduce(((t,r)=>(n.f[r](e,t),t)),[])),n.u=e=>"assets/js/"+({20:"977fb8cd",53:"935f2afb",61:"44ef08bc",78:"168e900d",109:"75a55335",162:"d589d3a7",195:"c4f5d8e4",217:"3b8c55ea",235:"f38b294d",241:"d8fa0e62",245:"3d955d88",271:"1d90b0b1",315:"e9c55326",385:"974039d5",437:"600fab28",476:"618f8fb4",498:"28b9547a",514:"1be78505",527:"ace312b1",571:"7958baa0",611:"a7da88a7",620:"927d9381",658:"43cd4838",675:"984db706",887:"d5efc89d",918:"17896441",920:"1a4e3797",960:"414a3798"}[e]||e)+"."+{20:"48a989ec",53:"2c4fc095",61:"9f344e37",78:"3c6dcb38",109:"70de1b18",162:"e6f49f4f",195:"46cfd268",217:"6aee7597",235:"1111032d",241:"fdc8a617",245:"7c2822bc",271:"a2e9df6a",315:"bf7580ff",385:"69facaf0",437:"48886f91",476:"80c6c655",498:"8b9149cd",514:"eda58c99",527:"a5b8073b",571:"7ccf143a",611:"b5f2b6f0",620:"ec232123",658:"10b358ab",675:"ef09508a",780:"7f861c8c",814:"bb50acf4",887:"cc162c4a",894:"662af41b",918:"4c0895fa",920:"ecc9196e",945:"e8a8f394",960:"3626043e",972:"af7814cb"}[e]+".js",n.miniCssF=e=>{},n.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),a={},o="docs:",n.l=(e,t,r,d)=>{if(a[e])a[e].push(t);else{var f,c;if(void 0!==r)for(var b=document.getElementsByTagName("script"),i=0;i{f.onerror=f.onload=null,clearTimeout(s);var o=a[e];if(delete a[e],f.parentNode&&f.parentNode.removeChild(f),o&&o.forEach((e=>e(r))),t)return t(r)},s=setTimeout(l.bind(null,void 0,{type:"timeout",target:f}),12e4);f.onerror=l.bind(null,f.onerror),f.onload=l.bind(null,f.onload),c&&document.head.appendChild(f)}},n.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.p="/boxed/",n.gca=function(e){return e={17896441:"918","977fb8cd":"20","935f2afb":"53","44ef08bc":"61","168e900d":"78","75a55335":"109",d589d3a7:"162",c4f5d8e4:"195","3b8c55ea":"217",f38b294d:"235",d8fa0e62:"241","3d955d88":"245","1d90b0b1":"271",e9c55326:"315","974039d5":"385","600fab28":"437","618f8fb4":"476","28b9547a":"498","1be78505":"514",ace312b1:"527","7958baa0":"571",a7da88a7:"611","927d9381":"620","43cd4838":"658","984db706":"675",d5efc89d:"887","1a4e3797":"920","414a3798":"960"}[e]||e,n.p+n.u(e)},(()=>{var e={303:0,532:0};n.f.j=(t,r)=>{var a=n.o(e,t)?e[t]:void 0;if(0!==a)if(a)r.push(a[2]);else if(/^(303|532)$/.test(t))e[t]=0;else{var o=new Promise(((r,o)=>a=e[t]=[r,o]));r.push(a[2]=o);var d=n.p+n.u(t),f=new Error;n.l(d,(r=>{if(n.o(e,t)&&(0!==(a=e[t])&&(e[t]=void 0),a)){var o=r&&("load"===r.type?"missing":r.type),d=r&&r.target&&r.target.src;f.message="Loading chunk "+t+" failed.\n("+o+": "+d+")",f.name="ChunkLoadError",f.type=o,f.request=d,a[1](f)}}),"chunk-"+t,t)}},n.O.j=t=>0===e[t];var t=(t,r)=>{var a,o,d=r[0],f=r[1],c=r[2],b=0;if(d.some((t=>0!==e[t]))){for(a in f)n.o(f,a)&&(n.m[a]=f[a]);if(c)var i=c(n)}for(t&&t(r);b{"use strict";var e,t,r,a,o,d={},f={};function n(e){var t=f[e];if(void 0!==t)return t.exports;var r=f[e]={id:e,loaded:!1,exports:{}};return d[e].call(r.exports,r,r.exports,n),r.loaded=!0,r.exports}n.m=d,n.c=f,e=[],n.O=(t,r,a,o)=>{if(!r){var d=1/0;for(i=0;i=o)&&Object.keys(n.O).every((e=>n.O[e](r[c])))?r.splice(c--,1):(f=!1,o0&&e[i-1][2]>o;i--)e[i]=e[i-1];e[i]=[r,a,o]},n.n=e=>{var t=e&&e.__esModule?()=>e.default:()=>e;return n.d(t,{a:t}),t},r=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,n.t=function(e,a){if(1&a&&(e=this(e)),8&a)return e;if("object"==typeof e&&e){if(4&a&&e.__esModule)return e;if(16&a&&"function"==typeof e.then)return e}var o=Object.create(null);n.r(o);var d={};t=t||[null,r({}),r([]),r(r)];for(var f=2&a&&e;"object"==typeof f&&!~t.indexOf(f);f=r(f))Object.getOwnPropertyNames(f).forEach((t=>d[t]=()=>e[t]));return d.default=()=>e,n.d(o,d),o},n.d=(e,t)=>{for(var r in t)n.o(t,r)&&!n.o(e,r)&&Object.defineProperty(e,r,{enumerable:!0,get:t[r]})},n.f={},n.e=e=>Promise.all(Object.keys(n.f).reduce(((t,r)=>(n.f[r](e,t),t)),[])),n.u=e=>"assets/js/"+({20:"977fb8cd",53:"935f2afb",61:"44ef08bc",78:"168e900d",109:"75a55335",162:"d589d3a7",195:"c4f5d8e4",217:"3b8c55ea",235:"f38b294d",241:"d8fa0e62",245:"3d955d88",271:"1d90b0b1",315:"e9c55326",385:"974039d5",437:"600fab28",476:"618f8fb4",498:"28b9547a",514:"1be78505",527:"ace312b1",571:"7958baa0",611:"a7da88a7",620:"927d9381",658:"43cd4838",675:"984db706",887:"d5efc89d",918:"17896441",920:"1a4e3797",960:"414a3798"}[e]||e)+"."+{20:"48a989ec",53:"2c4fc095",61:"9f344e37",78:"3c6dcb38",109:"70de1b18",162:"e6f49f4f",195:"f6e0632f",217:"6aee7597",235:"1111032d",241:"fdc8a617",245:"7c2822bc",271:"a2e9df6a",315:"bf7580ff",385:"4d46b89d",437:"48886f91",476:"66c10766",498:"8b9149cd",514:"eda58c99",527:"936ef1cd",571:"7ccf143a",611:"b5f2b6f0",620:"ec232123",658:"10b358ab",675:"ef09508a",780:"7f861c8c",814:"bb50acf4",887:"cc162c4a",894:"662af41b",918:"4c0895fa",920:"ecc9196e",945:"e8a8f394",960:"3626043e",972:"af7814cb"}[e]+".js",n.miniCssF=e=>{},n.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),n.o=(e,t)=>Object.prototype.hasOwnProperty.call(e,t),a={},o="docs:",n.l=(e,t,r,d)=>{if(a[e])a[e].push(t);else{var f,c;if(void 0!==r)for(var b=document.getElementsByTagName("script"),i=0;i{f.onerror=f.onload=null,clearTimeout(s);var o=a[e];if(delete a[e],f.parentNode&&f.parentNode.removeChild(f),o&&o.forEach((e=>e(r))),t)return t(r)},s=setTimeout(l.bind(null,void 0,{type:"timeout",target:f}),12e4);f.onerror=l.bind(null,f.onerror),f.onload=l.bind(null,f.onload),c&&document.head.appendChild(f)}},n.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},n.p="/boxed/",n.gca=function(e){return e={17896441:"918","977fb8cd":"20","935f2afb":"53","44ef08bc":"61","168e900d":"78","75a55335":"109",d589d3a7:"162",c4f5d8e4:"195","3b8c55ea":"217",f38b294d:"235",d8fa0e62:"241","3d955d88":"245","1d90b0b1":"271",e9c55326:"315","974039d5":"385","600fab28":"437","618f8fb4":"476","28b9547a":"498","1be78505":"514",ace312b1:"527","7958baa0":"571",a7da88a7:"611","927d9381":"620","43cd4838":"658","984db706":"675",d5efc89d:"887","1a4e3797":"920","414a3798":"960"}[e]||e,n.p+n.u(e)},(()=>{var e={303:0,532:0};n.f.j=(t,r)=>{var a=n.o(e,t)?e[t]:void 0;if(0!==a)if(a)r.push(a[2]);else if(/^(303|532)$/.test(t))e[t]=0;else{var o=new Promise(((r,o)=>a=e[t]=[r,o]));r.push(a[2]=o);var d=n.p+n.u(t),f=new Error;n.l(d,(r=>{if(n.o(e,t)&&(0!==(a=e[t])&&(e[t]=void 0),a)){var o=r&&("load"===r.type?"missing":r.type),d=r&&r.target&&r.target.src;f.message="Loading chunk "+t+" failed.\n("+o+": "+d+")",f.name="ChunkLoadError",f.type=o,f.request=d,a[1](f)}}),"chunk-"+t,t)}},n.O.j=t=>0===e[t];var t=(t,r)=>{var a,o,d=r[0],f=r[1],c=r[2],b=0;if(d.some((t=>0!==e[t]))){for(a in f)n.o(f,a)&&(n.m[a]=f[a]);if(c)var i=c(n)}for(t&&t(r);b AsyncData<Result<Ok, Error>> | Boxed - +

AsyncData<Result<Ok, Error>>

A AsyncData can contain a Result (e.g. to represent an asynchronous value that can fail). We provide some utility functions to deal with that case without having to unwrap the AsyncData result.

note

You can still use all the regular AsyncData methods. The following helpers simply removes the need to unwrap the contained result.

Methods

.mapOkToResult(f)

AsyncData<Result<A, E>>.mapOkToResult<B, F>(
func: (value: A) => Result<B, F>,
): AsyncData<Result<B, E | F>>

Takes a AsyncData<Result<Ok, Error>> and a f function taking Ok and returning Result<ReturnValue, Error> and returns a new AsyncData<Result<ReturnValue, Error>>

Examples
AsyncData.Done(Result.Ok(3)).mapOkToResult((ok) => {
return Result.Ok(ok * 2);
});
// AsyncData<Result.Ok<6>>

AsyncData.Done(Result.Ok(3)).mapOkToResult((ok) =>
isEven(ok) ? Result.Ok(ok) : Result.Error("Odd number");
);
// AsyncData<Result.Error<"Odd number">>

.mapErrorToResult(f)

AsyncData<Result<A, E>>.mapErrorToResult<B, F>(
func: (value: E) => Result<B, F>,
): AsyncData<Result<A | B, F>>

Takes a AsyncData<Result<Ok, Error>> and a f function taking Error and returning Result<ReturnValue, Error> and returns a new AsyncData<Result<ReturnValue, Error>>

Examples
AsyncData.Done(Result.Error(3)).mapErrorToResult((error) => {
return Result.Ok(ok * 2);
});
// AsyncData<Result.Ok<6>>

AsyncData.Done(Result.Error(3)).mapErrorToResult((error) =>
isEven(error) ? Result.Ok(error) : Result.Error("Odd number");
);
// AsyncData<Result.Error<"Odd number">>

.mapOk(f)

AsyncData<Result<A, E>>.mapOk<B>(
func: (value: A) => B,
): AsyncData<Result<B, E>>

Takes a AsyncData<Result<Ok, Error>> and a f function taking Ok and returning ReturnValue and returns a new AsyncData<Result<ReturnValue, Error>>

Examples
AsyncData.Done(Result.Ok(3)).mapOk((ok) => {
return ok * 2;
});
// AsyncData<Result.Ok<6>>

AsyncData.Done(Result.Error("something")).mapOk((ok) => {
return ok * 2;
});
// AsyncData<Result.Error<"something">>

.mapError(f)

AsyncData<Result<A, E>>.mapError<F>(
func: (value: E) => F,
): AsyncData<Result<A, F>>

Takes a AsyncData<Result<Ok, Error>> and a f function taking Error and returning ReturnValue and returns a new AsyncData<Result<Ok, ReturnValue>>

Examples
AsyncData.Done(Result.Error(3)).mapError((error) => {
return error * 2;
});
// AsyncData<Result.Error<6>>

AsyncData.Done(Result.Ok("something")).mapError((ok) => {
return ok * 2;
});
// AsyncData<Result.Ok<"something">>

.flatMapOk(f)

AsyncData<Result<A, E>>.flatMapOk<B, F>(
func: (value: A) => AsyncData<Result<B, F>>,
): AsyncData<Result<B, E | F>>

Takes a AsyncData<Result<Ok, Error>> and a f function taking Ok returning a AsyncData<Result<ReturnValue, Error>>

Examples
AsyncData.Done(Result.Ok(3)).flatMapOk((ok) =>
AsyncData.Done(Result.Ok(ok * 2)),
);
// AsyncData<Result.Ok<6>>

AsyncData.Done(Result.Ok(3)).flatMapOk((ok) =>
AsyncData.Done(Result.Error("Nope")),
);
// AsyncData<Result.Error<"Nope">>

AsyncData.Done(Result.Error("Error")).flatMapOk((ok) =>
AsyncData.Done(Result.Ok(ok * 2)),
);
// AsyncData<Result.Error<"Error">>

.flatMapError(f)

AsyncData<Result<A, E>>.flatMapError<B, F>(
func: (value: E) => AsyncData<Result<B, F>>,
): AsyncData<Result<A | B, F>>

Takes a AsyncData<Result<Ok, Error>> and a f function taking Error returning a AsyncData<Result<Ok, ReturnValue>>

Examples
AsyncData.Done(Result.Ok(3)).flatMapError((error) =>
AsyncData.Done(Result.Ok(ok * 2)),
);
// AsyncData<Result.Ok<3>>

AsyncData.Done(Result.Error("Error")).flatMapError((error) =>
AsyncData.Done(Result.Error("Nope")),
);
// AsyncData<Result.Error<"Nope">>

AsyncData.Done(Result.Error("Error")).flatMapError((error) =>
AsyncData.Done(Result.Ok(1)),
);
// AsyncData<Result.Ok<1>>

Cheatsheet

MethodInputFunction inputFunction outputReturned value
mapOkToResultAsyncData(Ok(x))xOk(y)AsyncData(Ok(y))
mapOkToResultAsyncData(Ok(x))xError(f)AsyncData(Error(f))
mapOkToResultAsyncData(Error(e))not providednot executedAsyncData(Error(e))
mapErrorToResultAsyncData(Error(e))eOk(y)AsyncData(Ok(y))
mapErrorToResultAsyncData(Error(e))eError(f)AsyncData(Error(f))
mapErrorToResultAsyncData(Ok(x))not providednot executedAsyncData(Ok(x))
mapOkAsyncData(Ok(x))xyAsyncData(Ok(y))
mapOkAsyncData(Error(e))not providednot executedAsyncData(Error(e))
mapErrorAsyncData(Ok(x))not providednot executedAsyncData(Ok(x))
mapErrorAsyncData(Error(e))efAsyncData(Error(f))
flatMapOkAsyncData(Ok(x))xAsyncData(Ok(y))AsyncData(Ok(y))
flatMapOkAsyncData(Ok(x))xAsyncData(Error(f))AsyncData(Error(f))
flatMapOkAsyncData(Error(e))not providednot executedAsyncData(Error(e))
flatMapErrorAsyncData(Ok(x))not providednot executedAsyncData(Ok(x))
flatMapErrorAsyncData(Error(e))eAsyncData(Ok(y))AsyncData(Ok(y))
flatMapErrorAsyncData(Error(e))eAsyncData(Error(f))AsyncData(Error(f))
- + \ No newline at end of file diff --git a/async-data/index.html b/async-data/index.html index 6854bc7..348b3d3 100644 --- a/async-data/index.html +++ b/async-data/index.html @@ -5,13 +5,13 @@ AsyncData<Value> | Boxed - +
-

AsyncData<Value>

The AsyncData type enables representing asynchronous flows (e.g. requests). The type represents the state as a discriminating union, avoiding manual management for loading flows.

AsyncData can have three possible states:

  • NotAsked
  • Loading
  • Done(value)

Create an AsyncData value

To create an async data, use the NotAsked, Loading and Done constructors:

Examples
import { AsyncData } from "@swan-io/boxed";

const notAsked = AsyncData.NotAsked();

const loading = AsyncData.Loading();

const done = AsyncData.Done(1);

Methods

The async data type provides a few manipulation functions:

.map(f)

AsyncData<A>.map<B>(f: (value: A) => B): AsyncData<B>

If the asyncData is Done(value) returns Done(f(value)), otherwise returns the async data.

Examples
AsyncData.Done(2).map((x) => x * 2);
// AsyncData.Done<4>

AsyncData.Loading().map((x) => x * 2);
// AsyncData.Loading

AsyncData.NotAsked().map((x) => x * 2);
// AsyncData.NotAsked

.flatMap(f)

AsyncData<A>.flatMap<B>(f: (value: A) => AsyncData<B>): AsyncData<B>

If the asyncData is Done(value) returns f(value), otherwise returns the async data.

Examples
AsyncData.Done(3).flatMap((x) =>
x > 2 ? AsyncData.NotAsked() : AsyncData.Done(2),
);
// AsyncData.NotAsked

AsyncData.Done(1).flatMap((x) =>
x > 2 ? AsyncData.NotAsked() : AsyncData.Done(2),
);
// AsyncData.Done<2>

AsyncData.NotAsked().flatMap((x) =>
x > 2 ? AsyncData.NotAsked() : AsyncData.Done(2),
);
// AsyncData.NotAsked

AsyncData.Loading().flatMap((x) =>
x > 2 ? AsyncData.NotAsked() : AsyncData.Done(2),
);
// AsyncData.Loading

.getOr(defaultValue)

Alias: getWithDefault

AsyncData<A>.getOr(defaultValue: A): A

If the async data is Done(value) returns value, otherwise returns defaultValue.

Examples
AsyncData.Done(2).getOr(1);
// 2

AsyncData.Loading().getOr(1);
// 1

AsyncData.NotAsked().getOr(1);
// 1

.get()

AsyncData<A>.get(): A

Returns the value contained in Done(value). Only usable within a isDone() check.

Examples
const value = asyncData.get();
// does not compile

if (asyncData.isDone()) {
const value = asyncData.get();
// value
}

.isDone()

AsyncData<A>.isDone(): boolean

Type guard. Checks if the option is Done(value)

Examples
AsyncData.Done(2).isDone();
// true

AsyncData.Loading().isDone();
// false

AsyncData.NotAsked().isDone();
// false

if (asyncData.isDone()) {
const value = asyncData.get();
}

.isLoading()

AsyncData<A>.isLoading(): boolean

Type guard. Checks if the option is Loading

Examples
AsyncData.Done(2).isLoading();
// false

AsyncData.Loading().isLoading();
// true

AsyncData.NotAsked().isLoading();
// false

.isNotAsked()

AsyncData<A>.isNotAsked(): boolean

Type guard. Checks if the option is NotAsked

Examples
AsyncData.Done(2).isNotAsked();
// false

AsyncData.Loading().isNotAsked();
// false

AsyncData.NotAsked().isNotAsked();
// true

.toOption()

AsyncData<A>.toOption(): Option<A>

If the result is Done(value) returns Some(value), otherwise returns None.

Examples
Result.Done(2).toOption();
// Option.Some<2>

Result.Loading().toOption();
// Option.None

Result.NotAsked().toOption();
// Option.None

.match()

AsyncData<A>.match<B>(config: {
Done: (value: A) => B;
Loading: () => B;
NotAsked: () => B;
}): B;

Match the async data state

Examples
const valueToDisplay = result.match({
Done: (value) => value,
Loading: () => "Loading ...",
NotAsked: () => "",
});

.tap(func)

AsyncData<A>.tap(func: (asyncData: AsyncData<A>) => unknown): AsyncData<A>

Executes func with asyncData, and returns asyncData. Useful for logging and debugging.

Examples
asyncData.tap(console.log).map((x) => x * 2);

Statics

AsyncData.isAsyncData(value)

isAsyncData(value: unknown): boolean

Type guard, checks if the provided value is an asyncData.

Examples
AsyncData.isAsyncData(AsyncData.Done(1));
// true

AsyncData.isAsyncData([]);
// false

AsyncData.all(asyncDatas)

all(asyncDatas: Array<AsyncData<A>>): AsyncData<Array<A>>

Turns an "array of asyncDatas of value" into a "asyncData of array of value".

Examples
AsyncData.all([AsyncData.Done(1), AsyncData.Done(2), AsyncData.Done(3)]);
// AsyncData.Done<[1, 2, 3]>

AsyncData.all([Result.NotAsked(), AsyncData.Done(2), AsyncData.Done(3)]);
// AsyncData.NotAsked

AsyncData.all([Result.Loading(), AsyncData.Done(2), AsyncData.Done(3)]);
// AsyncData.Loading

AsyncData.allFromDict(asyncDatas)

allFromDict(asyncDatas: Dict<AsyncData<A>>): AsyncData<Dict<A>>

Turns a "dict of asyncDatas of value" into a "asyncData of dict of value".

Examples
AsyncData.allFromDict({
a: AsyncData.Done(1),
b: AsyncData.Done(2),
c: AsyncData.Done(3),
});
// AsyncData.Done<{a: 1, b: 2, c: 3}>

AsyncData.allFromDict({
a: Result.NotAsked(),
b: AsyncData.Done(2),
c: AsyncData.Done(3),
});
// AsyncData.NotAsked

AsyncData.allFromDict({
a: Result.Loading(),
b: AsyncData.Done(2),
c: AsyncData.Done(3),
});
// AsyncData.Loading

TS Pattern interop

Examples
import { match, P } from "ts-pattern";
import { AsyncData } from "@swan-io/boxed";

match(asyncData)
.with(AsyncData.P.Done(P.select()), (value) => console.log(value))
.with(AsyncData.P.Loading, () => "Loading ...")
.with(AsyncData.P.NotAsked, () => "")
.exhaustive();

Cheatsheet

MethodInputFunction inputFunction outputReturned value
mapDone(x)xyDone(y)
mapLoading()not providednot executedLoading()
mapNotAsked()not providednot executedNotAsked()
flatMapDone(x)xDone(y)Done(y)
flatMapDone(x)xLoading()Loading()
flatMapDone(x)xNotAsked()NotAsked()
flatMapLoading()not providednot executedLoading()
flatMapNotAsked()not providednot executedNotAsked()
- +

AsyncData<Value>

The AsyncData type enables representing asynchronous flows (e.g. requests). The type represents the state as a discriminating union, avoiding manual management for loading flows.

AsyncData can have three possible states:

  • NotAsked
  • Loading
  • Done(value)

Create an AsyncData value

To create an async data, use the NotAsked, Loading and Done constructors:

Examples
import { AsyncData } from "@swan-io/boxed";

const notAsked = AsyncData.NotAsked();

const loading = AsyncData.Loading();

const done = AsyncData.Done(1);
Since v3.0.0

AsyncData values are referentially equal if they contain the same value, meaning that AsyncData.Done(1) === AsyncData.Done(1).

Methods

The async data type provides a few manipulation functions:

.map(f)

AsyncData<A>.map<B>(f: (value: A) => B): AsyncData<B>

If the asyncData is Done(value) returns Done(f(value)), otherwise returns the async data.

Examples
AsyncData.Done(2).map((x) => x * 2);
// AsyncData.Done<4>

AsyncData.Loading().map((x) => x * 2);
// AsyncData.Loading

AsyncData.NotAsked().map((x) => x * 2);
// AsyncData.NotAsked

.flatMap(f)

AsyncData<A>.flatMap<B>(f: (value: A) => AsyncData<B>): AsyncData<B>

If the asyncData is Done(value) returns f(value), otherwise returns the async data.

Examples
AsyncData.Done(3).flatMap((x) =>
x > 2 ? AsyncData.NotAsked() : AsyncData.Done(2),
);
// AsyncData.NotAsked

AsyncData.Done(1).flatMap((x) =>
x > 2 ? AsyncData.NotAsked() : AsyncData.Done(2),
);
// AsyncData.Done<2>

AsyncData.NotAsked().flatMap((x) =>
x > 2 ? AsyncData.NotAsked() : AsyncData.Done(2),
);
// AsyncData.NotAsked

AsyncData.Loading().flatMap((x) =>
x > 2 ? AsyncData.NotAsked() : AsyncData.Done(2),
);
// AsyncData.Loading

.getOr(defaultValue)

Alias: getWithDefault

AsyncData<A>.getOr(defaultValue: A): A

If the async data is Done(value) returns value, otherwise returns defaultValue.

Examples
AsyncData.Done(2).getOr(1);
// 2

AsyncData.Loading().getOr(1);
// 1

AsyncData.NotAsked().getOr(1);
// 1

.get()

AsyncData<A>.get(): A

Returns the value contained in Done(value). Only usable within a isDone() check.

Examples
const value = asyncData.get();
// does not compile

if (asyncData.isDone()) {
const value = asyncData.get();
// value
}

.isDone()

AsyncData<A>.isDone(): boolean

Type guard. Checks if the option is Done(value)

Examples
AsyncData.Done(2).isDone();
// true

AsyncData.Loading().isDone();
// false

AsyncData.NotAsked().isDone();
// false

if (asyncData.isDone()) {
const value = asyncData.get();
}

.isLoading()

AsyncData<A>.isLoading(): boolean

Type guard. Checks if the option is Loading

Examples
AsyncData.Done(2).isLoading();
// false

AsyncData.Loading().isLoading();
// true

AsyncData.NotAsked().isLoading();
// false

.isNotAsked()

AsyncData<A>.isNotAsked(): boolean

Type guard. Checks if the option is NotAsked

Examples
AsyncData.Done(2).isNotAsked();
// false

AsyncData.Loading().isNotAsked();
// false

AsyncData.NotAsked().isNotAsked();
// true

.toOption()

AsyncData<A>.toOption(): Option<A>

If the result is Done(value) returns Some(value), otherwise returns None.

Examples
Result.Done(2).toOption();
// Option.Some<2>

Result.Loading().toOption();
// Option.None

Result.NotAsked().toOption();
// Option.None

.match()

AsyncData<A>.match<B>(config: {
Done: (value: A) => B;
Loading: () => B;
NotAsked: () => B;
}): B;

Match the async data state

Examples
const valueToDisplay = result.match({
Done: (value) => value,
Loading: () => "Loading ...",
NotAsked: () => "",
});

.tap(func)

AsyncData<A>.tap(func: (asyncData: AsyncData<A>) => unknown): AsyncData<A>

Executes func with asyncData, and returns asyncData. Useful for logging and debugging.

Examples
asyncData.tap(console.log).map((x) => x * 2);

Statics

AsyncData.isAsyncData(value)

isAsyncData(value: unknown): boolean

Type guard, checks if the provided value is an asyncData.

Examples
AsyncData.isAsyncData(AsyncData.Done(1));
// true

AsyncData.isAsyncData([]);
// false

AsyncData.all(asyncDatas)

all(asyncDatas: Array<AsyncData<A>>): AsyncData<Array<A>>

Turns an "array of asyncDatas of value" into a "asyncData of array of value".

Examples
AsyncData.all([AsyncData.Done(1), AsyncData.Done(2), AsyncData.Done(3)]);
// AsyncData.Done<[1, 2, 3]>

AsyncData.all([Result.NotAsked(), AsyncData.Done(2), AsyncData.Done(3)]);
// AsyncData.NotAsked

AsyncData.all([Result.Loading(), AsyncData.Done(2), AsyncData.Done(3)]);
// AsyncData.Loading

AsyncData.allFromDict(asyncDatas)

allFromDict(asyncDatas: Dict<AsyncData<A>>): AsyncData<Dict<A>>

Turns a "dict of asyncDatas of value" into a "asyncData of dict of value".

Examples
AsyncData.allFromDict({
a: AsyncData.Done(1),
b: AsyncData.Done(2),
c: AsyncData.Done(3),
});
// AsyncData.Done<{a: 1, b: 2, c: 3}>

AsyncData.allFromDict({
a: Result.NotAsked(),
b: AsyncData.Done(2),
c: AsyncData.Done(3),
});
// AsyncData.NotAsked

AsyncData.allFromDict({
a: Result.Loading(),
b: AsyncData.Done(2),
c: AsyncData.Done(3),
});
// AsyncData.Loading

TS Pattern interop

Examples
import { match, P } from "ts-pattern";
import { AsyncData } from "@swan-io/boxed";

match(asyncData)
.with(AsyncData.P.Done(P.select()), (value) => console.log(value))
.with(AsyncData.P.Loading, () => "Loading ...")
.with(AsyncData.P.NotAsked, () => "")
.exhaustive();

Cheatsheet

MethodInputFunction inputFunction outputReturned value
mapDone(x)xyDone(y)
mapLoading()not providednot executedLoading()
mapNotAsked()not providednot executedNotAsked()
flatMapDone(x)xDone(y)Done(y)
flatMapDone(x)xLoading()Loading()
flatMapDone(x)xNotAsked()NotAsked()
flatMapLoading()not providednot executedLoading()
flatMapNotAsked()not providednot executedNotAsked()
+ \ No newline at end of file diff --git a/cancellable-request/index.html b/cancellable-request/index.html index 13c7cd9..2d0c653 100644 --- a/cancellable-request/index.html +++ b/cancellable-request/index.html @@ -5,13 +5,13 @@ Cancellable Request | Boxed - +

Cancellable Request

When using the naive fetch, cancelling a request can be inelegant:

import { useEffect } from "react";
import { Result } from "@swan-io/boxed";

const callMyApi = (url, { signal }) => {
return fetch(url, { signal })
.then((res) => res.json())
.then((json) => Result.Ok(res))
.catch((error) => Result.Error(error));
};

// ...
useEffect(() => {
// Implementation details leak to your components
const controller = new AbortController();
callMyApi("/users", { signal: controller.signal })
.then((res) => res.json())
.then((json) => setState(Result.Ok(json)))
.catch((error) => setState(Result.Error(error)));
return () => controller.abort();
}, []);

Using Future can make this easier:

import { useEffect } from "react";
import { Future, Result } from "@swan-io/boxed";

const callMyApi = (url: string) =>
Future.make((resolve) => {
const controller = new AbortController();

fetch(url, { signal: controller.signal })
.then((res) => res.json())
.then((json) => resolve(Result.Ok(json)))
.catch((error) => resolve(Result.Error(error)));

// Here, the implementation detail is managed in place
return () => controller.abort();
});

// And the noise dissapears from your components!
useEffect(() => {
const request = callMyApi("/api").tap(setState);
return () => request.cancel();
}, []);
- + \ No newline at end of file diff --git a/core-concepts/index.html b/core-concepts/index.html index 2232c13..03d092f 100644 --- a/core-concepts/index.html +++ b/core-concepts/index.html @@ -5,13 +5,13 @@ Core Concepts | Boxed - +

Core Concepts

The Boxed approach takes root in typed functional paradigms. We know that these concepts can be overwhelming, especially with their jargon and mathematical concepts, and therefore want to make them more accessible.

As beautiful and powerful these concepts are, they come with a huge learning curve that we want to avoid. We also want your code to be simple to read, write and reason about without having to know the full theory behind.

danger

If you have a strong opinion on what metaphor to use to describe monads, please stop reading this page immediately and visit the API reference.

Schrödinger's cat

When physicists discovered that particules do weird stuff, they decided that while we don't look at them, they're in a superposition of states.

Erwin Schrödinger didn't like that one bit and decided to show them how utterly stupid it was. For that, he created a thought experiment now called the Schrödinger's cat experiment, in which a cat is put into a box (roll credits) with a cat-killing device reacting to some quantum stuff. While we don't open the box, the cat is both alive and dead as the same time, when we open the box, we fix an outcome.

Schrödinger&#39;s cat experiment

info

We love cats and the boxes used in the following explanations will only contain JavaScript values.

Boxes

The way we like to think of the data-structures we expose are that they're boxes (think of it as containers) that may or may not contain a value.

Here's a visual example using the Option type. The Option represents an optional value, it can have two possible states: either Some(value) or None().

Option of blue circle, it can be either a box containing a blue circle, which we call Some blue circle, or an empty box, which we call None

Option is a generic type, meaning you can define what type of value it holds: Option<User>, Option<string> (just like an array: Array<User>, Array<string>).

In your program flow, you don't know what's inside, as if the box was closed.

When you extract the value from the box, you have a few options:

// Let's assument we have `option` be of type `Option<number>`

// Returns the value if present or the fallback otherwise
const a = option.getOr(0);

// Explode the box
const b = option.match({
Some: (value) => value,
None: () => 0,
});

Data-manipulation basics

Most of the data-manipulation you'll do comes down to two function: map and flatMap.

Here's a visual explanation:

The map function transforms the value with a callback, the flatMap function returns an box itself

The map and flatMap functions allow you to transform data in a typesafe way:

const some = Option.Some(1);
// Option.Some<1>

const none = Option.None();
// Option.None

const doubledSome = some.map((x) => x * 2);
// Option.Some<2>

const doubledNone = none.map((x) => x * 2);
// Option.None -> Nothing to transform!

The flatMap lets you return another option, which can be useful for nested optional values:

type UserInfo = {
name: Option<string>;
};

type User = {
id: string;
info: Option<UserInfo>;
};

const name = user
.flatMap((user) => user.info) // Returns the Option<UserInfo>
.flatMap((info) => info.name) // Returns the Option<string>
.getOr("Anonymous user");

The main kind of boxes

Option<Value>

Represents optional values:

  • Replaces undefined and null
  • Makes it possible to differentiate nested optionality (Some(None()) vs None())
  • Reduces the number of codepaths needed to read and transform such values

Result<Ok, Error>

Represents a computation that can either succeed or fail:

  • Replaces exceptions
  • Allows you to have a single codepath instead or "return or throw".
  • Makes it easy to aggregate all possible errors a stack can generate

AsyncData<Value>

Represents a value with an asynchronous lifecycle:

  • Eliminates impossibles cases in your state
  • Avoids inconsistent states induced by traditional modeling
  • Allows you to tie the lifecycle information with the value itself

Future<Value>

Represents an asynchronous value:

  • Replaces promises
  • Supports cancellation
  • Delegates success/failure to the Result type
  • Exposes a map & flatMap API
- + \ No newline at end of file diff --git a/deferred/index.html b/deferred/index.html index 0674d02..7e20da7 100644 --- a/deferred/index.html +++ b/deferred/index.html @@ -5,13 +5,13 @@ Deferred<Value> | Boxed - +

Deferred<Value>

Deferred.make()

Returns a future and its resolver:

import { Deferred } from "@swan-io/boxed";

const [future, resolve] = Deferred.make<string>();

// subscribe to the promise
future.onResolve(console.log);

// resolve from elsewhere
resolve("Hello!");
- + \ No newline at end of file diff --git a/design-choices/index.html b/design-choices/index.html index 6581bfe..a491140 100644 --- a/design-choices/index.html +++ b/design-choices/index.html @@ -5,13 +5,13 @@ Design choices | Boxed - +

Design choices

Chaining API

Most functional programming libraries in JavaScript provide a data-last API (map(func)(value)) along with a pipe function to chain operations.

While this approach works well in languages that include some kind of native pipe operator, we consider that it doesn't provide a good enough developer experience in your TypeScript code editor: it makes it tedious to inspect a value in a pipe chain and doesn't provide autocomplete natively.

For this reason, we decided to use good ol'chaining, reducing the number of imports, making your code more expressive, easier to read and inspect.

Eager over lazy

More often than not, these functional programming libraries use lazy initialisation, meaning your code won't execute until you tell it to explicitely (e.g. with a run call at the end).

In the vast majority of cases we've seen, this is not something that's wanted. For the few cases where laziness is wanted, making a function that returns the data-structure or using a Deferred does the job pretty well.

const eagerFuture = Future.make((resolve) => resolve(1));

const lazyFuture = () => Future.make((resolve) => resolve(1));

const [deferred, resolve] = Deferred.make<number>();

Naming

Rather than using naming from abstract theory, if a concept exists in JavaScript built-ins, Boxed will provide similar naming (e.g. we provide Future.all to mimic Promise.all rather than Future.sequenceArray).

- + \ No newline at end of file diff --git a/dict/index.html b/dict/index.html index 7983c86..7797641 100644 --- a/dict/index.html +++ b/dict/index.html @@ -5,13 +5,13 @@ Dict | Boxed - +

Dict

import { Dict } from "@swan-io/boxed";

Dict.entries(dict)

Returns the entries in the dict.

Contrary to the TS bindings for Object.entries, the types are refined.

Examples
const index = Dict.entries({ foo: 1, bar: 2, baz: 3 });
// [["foo", 1], ["bar", 2], ["baz", 3]];

Dict.fromEntries(entries)

Returns a dict from the provided [key, value] pairs.

Examples
const dict = Dict.fromEntries([
["foo", 1],
["bar", 2],
["baz", 3],
]);
// { foo: 1, bar: 2, baz: 3 };

Dict.keys(dict)

Returns the keys in the dict.

Contrary to the TS bindings for Object.keys, the types are refined.

Examples
const index = Dict.keys({ foo: 1, bar: 2, baz: 3 });
// ["foo", "bar", "baz"];

Dict.values(dict)

Returns the values in the dict.

Contrary to the TS bindings for Object.values, the types are refined.

Examples
const index = Dict.values({ foo: 1, bar: 2, baz: 3 });
// [1, 2, 3];

Dict.fromOptional(dictOfOptions)

Takes a dict whose values are Option<unknown> and returns a dict containing only the values contained in Some.

Examples
Dict.fromOptional({
foo: Option.Some(1),
bar: Option.None(),
baz: Option.None(),
});
// {foo: 1}

Dict.fromOptional({
foo: Option.Some(1),
bar: Option.Some(2),
baz: Option.None(),
});
// {foo: 1, bar: 2}

Dict.fromOptional({
foo: Option.None(),
bar: Option.None(),
baz: Option.None(),
});
// {}
- + \ No newline at end of file diff --git a/form-validation/index.html b/form-validation/index.html index 907d086..c7ef0e0 100644 --- a/form-validation/index.html +++ b/form-validation/index.html @@ -5,13 +5,13 @@ Form Validation | Boxed - +

Form Validation

A common need in applications is to validate user-input before sending it to the server.

Let's assume we have a form with the following fields:

type FormInput = {
id: string;
amount: number;
};

One can use exceptions:

const validate = (input: FormInput) => {
if (input.id.trim().length !== 24) {
throw new Error("Input ID is invalid");
}
if (input.amount <= 0) {
throw new Error("Invalid amount");
}
};

In that case, we'd use a try statement:

try {
const sanitized = sanitize(input);
validate(sanitized);
setValidation(null);

// send to the server
} catch (err) {
setValidation(err);
}

Or one can return errors from the validate function:

const validate = (input: FormInput) => {
const errors = [];
if (input.id.trim().length !== 24) {
errors.push("Input ID is invalid");
}
if (input.amount <= 0) {
errors.push("Invalid amount");
}
return errors;
};

Which would be consumed like the following:

const sanitized = sanitize(input);
const errors = validate(sanitized);
if (errors.length) {
setValidation(errors);
// show the errors
} else {
setValidation(null);
// send to the server
}

In both cases, we are required to have handle the validation state manually, which increases complexity and can lead to UI inconsistencies. Let's see how we can leverage the Result type for such patterns:

import { Result } from "@swan-io/boxed";

const validate = (input: FormInput): Result<FormInput, Array<string>> => {
const errors = [];
const id = input.id.trim();
if (id.length !== 24) {
errors.push("Input ID is invalid");
}
if (input.amount <= 0) {
errors.push("Invalid amount");
}

// We can directly return a sanitized version if the validation passed
return errors.length === 0
? Result.Ok({ ...input, id })
: Result.Error(errors);
};

Here, the validate return value can directly give you the sanitized input or the validation errors, depending on which case you're in.

We can then store the Result directly:

// A single codepath for handling the validation
const validation = validate(input);

setValidation(validation);

validation.match({
Error: () => {} // do nothing
Ok: (sanitizedInput) => {
sendToServer(sanitizedInput)
}
})

// and pattern match in the UI code
<Input
name="id"
value={input.id}
onChange={id => setInput({...input, id}))}
hasError={validation.match({
Ok: () => false,
Error: (errors) => errors.includes("Input ID is invalid"),
})}
/>;
- + \ No newline at end of file diff --git a/future-result/index.html b/future-result/index.html index b0ffcdf..40dd3c3 100644 --- a/future-result/index.html +++ b/future-result/index.html @@ -5,13 +5,13 @@ Future<Result<Ok, Error>> | Boxed - +

Future<Result<Ok, Error>>

A Future can contain a Result (e.g. to represent an asynchronous value that can fail). We provide some utility functions to deal with that case without having to unwrap the Future result.

note

You can still use all the regular Future methods. The following helpers simply removes the need to unwrap the contained result.

Methods

.mapOkToResult(f)

Future<Result<A, E>>.mapOkToResult<B, F>(
func: (value: A) => Result<B, F>,
propagateCancel?: boolean
): Future<Result<B, E | F>>

Takes a Future<Result<Ok, Error>> and a f function taking Ok and returning Result<ReturnValue, Error> and returns a new Future<Result<ReturnValue, Error>>

Examples
Future.value(Result.Ok(3)).mapOkToResult((ok) => {
return Result.Ok(ok * 2);
});
// Future<Result.Ok<6>>

Future.value(Result.Ok(3)).mapOkToResult((ok) =>
isEven(ok) ? Result.Ok(ok) : Result.Error("Odd number");
);
// Future<Result.Error<"Odd number">>

.mapErrorToResult(f)

Future<Result<A, E>>.mapErrorToResult<B, F>(
func: (value: E) => Result<B, F>,
propagateCancel?: boolean
): Future<Result<A | B, F>>

Takes a Future<Result<Ok, Error>> and a f function taking Error and returning Result<Ok, ReturnError> and returns a new Future<Result<Ok, ReturnError>>

Examples
Future.value(Result.Error(3)).mapErrorToResult((ok) => {
return Result.Ok(ok * 2);
});
// Future<Result.Ok<6>>

Future.value(Result.Error(3)).mapErrorToResult((ok) =>
isEven(ok) ? Result.Ok(ok) : Result.Error("Odd number");
);
// Future<Result.Error<"Odd number">>

.mapOk(f)

Future<Result<A, E>>.mapOk<B>(
func: (value: A) => B,
propagateCancel?: boolean
): Future<Result<B, E>>

Takes a Future<Result<Ok, Error>> and a f function taking Ok and returning ReturnValue and returns a new Future<Result<ReturnValue, Error>>

Examples
Future.value(Result.Ok(3)).mapOk((ok) => {
return ok * 2;
});
// Future<Result.Ok<6>>

Future.value(Result.Error("something")).mapOk((ok) => {
return ok * 2;
});
// Future<Result.Error<"something">>

.mapError(f)

Future<Result<A, E>>.mapError<F>(
func: (value: E) => F,
propagateCancel?: boolean
): Future<Result<A, F>>

Takes a Future<Result<Ok, Error>> and a f function taking Error and returning ReturnValue and returns a new Future<Result<Ok, ReturnValue>>

Examples
Future.value(Result.Error(3)).mapError((error) => {
return error * 2;
});
// Future<Result.Error<6>>

Future.value(Result.Ok("something")).mapError((ok) => {
return ok * 2;
});
// Future<Result.Ok<"something">>

.flatMapOk(f)

Future<Result<A, E>>.flatMapOk<B, F>(
func: (value: A) => Future<Result<B, F>>,
propagateCancel?: boolean
): Future<Result<B, E | F>>

Takes a Future<Result<Ok, Error>> and a f function taking Ok returning a Future<Result<ReturnValue, Error>>

Examples
Future.value(Result.Ok(3)).flatMapOk((ok) => Future.value(Result.Ok(ok * 2)));
// Future<Result.Ok<6>>

Future.value(Result.Ok(3)).flatMapOk((ok) =>
Future.value(Result.Error("Nope")),
);
// Future<Result.Error<"Nope">>

Future.value(Result.Error("Error")).flatMapOk((ok) =>
Future.value(Result.Ok(ok * 2)),
);
// Future<Result.Error<"Error">>

.flatMapError(f)

Future<Result<A, E>>.flatMapError<B, F>(
func: (value: E) => Future<Result<B, F>>,
propagateCancel?: boolean
): Future<Result<A | B, F>>

Takes a Future<Result<Ok, Error>> and a f function taking Error returning a Future<Result<Ok, ReturnValue>>

Examples
Future.value(Result.Ok(3)).flatMapError((error) =>
Future.value(Result.Ok(ok * 2)),
);
// Future<Result.Ok<3>>

Future.value(Result.Error("Error")).flatMapError((error) =>
Future.value(Result.Error("Nope")),
);
// Future<Result.Error<"Nope">>

Future.value(Result.Error("Error")).flatMapError((error) =>
Future.value(Result.Ok(1)),
);
// Future<Result.Ok<1>>

.tapOk(f)

Future<Result<A, E>>.tapOk(func: (value: A) => unknown): Future<Result<A, E>>

Runs f if value is Ok with the future value, and returns the original future. Useful for debugging.

Examples
future.tapOk(console.log);

.tapError(f)

Future<Result<A, E>>.tapError(func: (value: E) => unknown): Future<Result<A, E>>

Runs f if value is Error with the future value, and returns the original future. Useful for debugging.

Examples
future.tapError(console.log);

.resultToPromise()

Future<Result<A, E>>.resultToPromise(): Promise<A>

Takes a Future<Result<Ok, Error>> and returns a Promise<Ok>, rejecting the promise with Error in this state.

Examples
Future.value(Result.Ok(1)).resultToPromise();
// Promise<1>

Future.value(Result.Reject(1)).resultToPromise();
// Promise (rejected with 1)

Statics

Future.all(resultFutures)

You can combine the all helpers from Future and Result:

Examples
const futures = [
Future.value(Result.Ok(1)),
Future.value(Result.Ok(2)),
Future.value(Result.Ok(3)),
];

Future.all(futures).map(Result.all);
// Future<Result.Ok<[1, 2, 3]>>

Let's see the types at each step:

Examples
// Array<Future<Result<number, never>>>
// -> [Future<Result.Ok<1>>, Future<Result.Ok<2>>, Future<Result.Ok<3>>]
const input = [
Future.value(Result.Ok(1)),
Future.value(Result.Ok(2)),
Future.value(Result.Ok(3)),
];

// Future<Array<Result<number, never>>>
// -> Future<[Result.Ok<1>>, Result.Ok<2>>, Result.Ok<3>]>
const step1 = Future.all(input);

// Future<Result<Array<number>, never>>
// -> Future<[Result.Ok<[1, 2, 3]>>
const step2 = step1.map(Result.all);

Future.allFromDict(resultFutures)

Like as all, you can combine the allFromDict:

Examples
const futures = {
a: Future.value(Result.Ok(1)),
b: Future.value(Result.Ok(2)),
c: Future.value(Result.Ok(3)),
};

Future.allFromDict(futures).map(Result.allFromDict);
// Future<[Result.Ok<{a: 1, b: 2, c: 3}>>

Let's see the types at each step:

Examples
// Dict<Future<Result<number, never>>>
// -> {a: Future<Result.Ok<1>>, b: Future<Result.Ok<2>>, c: Future<Result.Ok<3>>—
const input = {
a: Future.value(Result.Ok(1)),
b: Future.value(Result.Ok(2)),
c: Future.value(Result.Ok(3)),
};

// Future<Dict<Result<number, never>>>
// -> Future<{a: Result.Ok<1>>, b: Result.Ok<2>>, c: Result.Ok<3>}>
const step1 = Future.all(input);

// Future<Result<Array<number>, never>>
// -> Future<[Result.Ok<{a: 1, b: 2, c: 3}>>
const step2 = step1.map(Result.all);

Future.retry(getFuture)

retry(getFuture: () => Future<Result<A, E>>, {max: number}): Future<Result<A, E>>

Runs the future getter, if the future resolves with a Result.Error, retries until hitting max attempts.

The function provides a 0-based attempt count to the function if you need to implement delay logic.

Examples
// retry immediately after failure
Future.retry(
(attempt) => {
return getUserById(userId);
},
{ max: 3 },
);
// Future<Result<...>>

// adding delay
Future.retry(
(attempt) => {
return Future.wait(attempt * 100).flatMap(() => getUserById(userId));
},
{ max: 10 },
);
// Future<Result<...>>

Cheatsheet

MethodInputFunction inputFunction outputReturned value
mapOkToResultFuture(Ok(x))xOk(y)Future(Ok(y))
mapOkToResultFuture(Ok(x))xError(f)Future(Error(f))
mapOkToResultFuture(Error(e))not providednot executedFuture(Error(e))
mapErrorToResultFuture(Error(e))eOk(y)Future(Ok(y))
mapErrorToResultFuture(Error(e))eError(f)Future(Error(f))
mapErrorToResultFuture(Ok(x))not providednot executedFuture(Ok(x))
mapOkFuture(Ok(x))xyFuture(Ok(y))
mapOkFuture(Error(e))not providednot executedFuture(Error(e))
mapErrorFuture(Ok(x))not providednot executedFuture(Ok(x))
mapErrorFuture(Error(e))efFuture(Error(f))
flatMapOkFuture(Ok(x))xFuture(Ok(y))Future(Ok(y))
flatMapOkFuture(Ok(x))xFuture(Error(f))Future(Error(f))
flatMapOkFuture(Error(e))not providednot executedFuture(Error(e))
flatMapErrorFuture(Ok(x))not providednot executedFuture(Ok(x))
flatMapErrorFuture(Error(e))eFuture(Ok(y))Future(Ok(y))
flatMapErrorFuture(Error(e))eFuture(Error(f))Future(Error(f))
- + \ No newline at end of file diff --git a/future/index.html b/future/index.html index 068a847..0942198 100644 --- a/future/index.html +++ b/future/index.html @@ -5,13 +5,13 @@ Future<Value> | Boxed - +

Future<Value>

The Future is a replacement for Promise.

Main differences with Promises

  • Futures don't handle rejection state, instead leaving it to a contained Result
  • Futures have built-in cancellation (and don't reject like the fetch signal API does)
  • Futures don't "swallow" futures that are returned from map and flatMap
  • Future callbacks run synchronously
info

Even though we're diverging from Promise, you can await a Future.

Create a Future

Examples
import { Future } from "@swan-io/boxed";

// Value
const future = Future.value(1);

// Simple future
const otherFuture = Future.make((resolve) => {
resolve(1);
});

// Future with cancellation effect
const otherFuture = Future.make((resolve) => {
const timeoutId = setTimeout(() => {
resolve(1);
}, 1000);
return () => clearTimeout(timeoutId);
});

Methods

.onResolve(f)

Future<A>.onResolve(func: (value: A) => void): void

Runs f with the future value as argument when available.

Examples
Future.value(1).onResolve(console.log);
// Log: 1

.onCancel(f)

Future<A>.onCancel(func: () => void): void

Runs f when the future is cancelled.

Examples
future.onCancel(() => {
// do something
});

.map(f)

Future<A>.map<B>(func: (value: A) => B, propagateCancel?: boolean): Future<B>

Takes a Future<A> and returns a new Future<f<A>>

Examples
Future.value(3).map((x) => x * 2);
// Future<6>

.flatMap(f)

Future<A>.flatMap<B>(func: (value: A) => Future<B>, propagateCancel?: boolean): Future<B>

Takes a Future<A>, and returns a new future taking the value of the future returned by f(A)

Examples
Future.value(3).flatMap((x) => Future.value(x * 2));
// Future<6>

.tap(f)

Future<A>.tap(func: (value: A) => unknown): Future<A>

Runs f with the future value, and returns the original future. Useful for debugging.

Examples
Future.value(3).tap(console.log);
// Log: 3
// Future<3>

.toPromise()

Future<A>.toPromise(): Promise<A>

Takes a Future<T> and returns a Promise<T>

Examples
Future.value(1).toPromise();
// Promise<1>

Future<Result<Ok, Error>>

We provide some special helpers for Futures containing a Result.

Statics

Future.isFuture(value)

isFuture(value: unknown): boolean

Type guard, checks if the provided value is a future.

Examples
Future.isFuture(Future.value(1));
// true

Future.isFuture([]);
// false

Future.all(futures)

all(futures: Array<Future<A>>): Future<Array<A>>

Turns an "array of futures of values" into a "future of array of value".

Examples
Future.all([Future.value(1), Future.value(2), Future.value(3)]);
// Future<[1, 2, 3]>

Future.concurrent(futureGetters, options)

all(futures: Array<() => Future<A>>, {concurrency: number}): Future<Array<A>>

Like Future.all with a max concurrency, and in order to control the flow, provided with functions returning futures.

Examples
Future.concurrent(
userIds.map((userId) => {
// notice we return a function
return () => getUserById(userId);
}),
{ concurrency: 10 },
);
// Future<[...]>

Future.wait(ms)

wait(ms: number): Future<void>

Helper to create a future that resolves after ms (in milliseconds).

Examples
Future.wait(1000).tap(() => console.log("Hey"));
// Logs "Hey" after 1s

Future.allFromDict(futures)

allFromDict(futures: Dict<Future<A>>): Future<Dict<A>>

Turns a "dict of futures of values" into a "future of dict of value".

Examples
Future.allFromDict({
a: Future.value(1),
b: Future.value(2),
c: Future.value(3),
});
// Future<{a: 1, b: 2, c: 3}>

Future.fromPromise(promise)

fromPromise<A>(promise: Promise<A>): Future<Result<A, unknown>>

Takes a Promise<T> and returns a Future<Result<T, Error>>

Examples
Future.fromPromise(Promise.resolve(1));
// Future<Result.Ok<1>>

Future.fromPromise(Promise.reject(1));
// Future<Result.Error<1>>

Cancellation

Basics

In JavaScript, Promises are not cancellable.

That can be limiting at times, especially when using React's useEffect, that let's you return a cancellation effect in order to prevent unwanted side-effects.

You can return a cleanup effect from the future init function:

const future = Future.make((resolve) => {
const timeoutId = setTimeout(() => {
resolve(1);
}, 1000);
// will run on cancellation
return () => clearTimeout(timeoutId);
});

To cancel a future, call future.cancel().

future.cancel();
note

You can only cancel a pending future.

Calling cancel on a resolved future is a no-op, meaning the future will keep its resolved state.

A cancelled future will automatically cancel any future created from it (e.g. from .map or .flatMap):

const future = Future.make((resolve) => {
const timeoutId = setTimeout(() => {
resolve(1);
}, 1000);
// will run on cancellation
return () => clearTimeout(timeoutId);
});

const future2 = future.map((x) => x * 2);

future.cancel(); // Both `future` and `future2` are cancelled

Bubbling cancellation

All .map* and .flatMap* methods take an extra parameter called propagateCancel, it enables the returned future cancel to bubble up cancellation to its depedencies:

// disabled by default: cancelling `future2` will not cancel `future`
const future2 = future.map((x) => x * 2);

// optin: cancelling `future2` will cancel `future`
const future2 = future.map((x) => x * 2, true);

This can be useful at call site:

const request = apiCall().map(parse, true);

request.cancel(); // will run the cleanup effect in `apiCall`

Cheatsheet

MethodInputFunction inputFunction outputReturned value
mapFuture(x)xyFuture(y)
flatMapFuture(x)xFuture(y)Future(y)
- + \ No newline at end of file diff --git a/getting-started/index.html b/getting-started/index.html index 41aa762..0218a66 100644 --- a/getting-started/index.html +++ b/getting-started/index.html @@ -5,13 +5,13 @@ Getting started | Boxed - +

Getting started

Boxed provides essential building-blocks to solve common issues you can run into in your application or library development.

What does Boxed solve?

Virtually any application has to deal with the following states:

  • optionality (a value being there or not)
  • success (a value that can be computed or fails to be)
  • completion (a value that is available or not)

If we use the default way JavaScript (and by extension TypeScript) provides to handle these, we generally end up with code that's growing more complex over time, and introduce subtle bugs:

A subtle bug

How does Boxed solve these bugs?

Boxed provides useful data-structures designed in way that completely eliminates these issues using properties from maths™ (don't worry, you don't need to know the full theory, you can just enjoy the benefits it provides).

Monads &amp; functors

Now, let's get into the details with the core concepts.

- + \ No newline at end of file diff --git a/index.html b/index.html index f93b3d4..3e4b199 100644 --- a/index.html +++ b/index.html @@ -5,13 +5,13 @@ Boxed: Essential building-blocks for functional & safe TypeScript code | Boxed - +
-
Boxed logo

Boxed

Essential building-blocks for functional & safe TypeScript code

import { AsyncData } from "@swan-io/boxed";

const UserCard = ({user}: {user: AsyncData<User>}) => {
return user.match({
NotAsked: () => null,
Loading: () => `Loading`,
Done: (user) => {
const name = user.name.getOr("anonymous");
return `Hello ${name}!`;
},
});
};

Avoid accidental complexity

Boxed provides functional building blocks that make your code more maintainable, more expressive, and safer.

Focused on DX

We provide a very small API surface. With easy interop, and compatibility with the ecosystem (like ts-pattern)

Easy to reason about

The concepts exposed by Boxed are simple and accessible: you don't need a CS degree to get started.

Example of a request lifecycle management in a React component using Boxed.AsyncData

Build with the right tools

By using functional type-safe constructs like Option, Result and AsyncData, you can eliminate bugs right from the modeling.

Your code will be simpler, safer,and easier to reason about than with regular null-checks, exception flows and manual value tracking.

All of that for less that 3KBs when gzipped!

Tailored for your IDE

Thanks to our chaining API, you get a nice autocomplete right from the value and can easily name intermediate variables.

Boxed leverages the JavaScript class API so that you don't need to import any module to work with a Boxed value: it's all available as a method. On top of that, the Boxed API is minimal, so that your tooling doesn't feel overwhelming.

Cheatsheet table for the types of the map and flatMap functions

Get productive immediately

Boxed gives you the tools you need without requiring loads of theoretical knowledge.

We provide simple naming, documentation and escape hatches so that you don't get stuck. You get to learn as you use the library instead of getting frustrated over complex concepts.

- +
Boxed logo

Boxed

Essential building-blocks for functional & safe TypeScript code

import { AsyncData } from "@swan-io/boxed";

const UserCard = ({user}: {user: AsyncData<User>}) => {
return user.match({
NotAsked: () => null,
Loading: () => `Loading`,
Done: (user) => {
const name = user.name.getOr("anonymous");
return `Hello ${name}!`;
},
});
};

Avoid accidental complexity

Boxed provides functional building blocks that make your code more maintainable, more expressive, and safer.

Focused on DX

We provide a very small API surface. With easy interop, and compatibility with the ecosystem (like ts-pattern)

Easy to reason about

The concepts exposed by Boxed are simple and accessible: you don't need a CS degree to get started.

Example of a request lifecycle management in a React component using Boxed.AsyncData

Build with the right tools

By using functional type-safe constructs like Option, Result and AsyncData, you can eliminate bugs right from the modeling.

Your code will be simpler, safer,and easier to reason about than with regular null-checks, exception flows and manual value tracking.

All of that for less that 3KBs when gzipped!

Tailored for your IDE

Thanks to our chaining API, you get a nice autocomplete right from the value and can easily name intermediate variables.

Boxed leverages the JavaScript class API so that you don't need to import any module to work with a Boxed value: it's all available as a method. On top of that, the Boxed API is minimal, so that your tooling doesn't feel overwhelming.

Cheatsheet table for the types of the map and flatMap functions

Get productive immediately

Boxed gives you the tools you need without requiring loads of theoretical knowledge.

We provide simple naming, documentation and escape hatches so that you don't get stuck. You get to learn as you use the library instead of getting frustrated over complex concepts.

+ \ No newline at end of file diff --git a/inspirations/index.html b/inspirations/index.html index 6b8d420..7e284dd 100644 --- a/inspirations/index.html +++ b/inspirations/index.html @@ -5,13 +5,13 @@ Inspirations | Boxed - + - + \ No newline at end of file diff --git a/installation/index.html b/installation/index.html index 95a05a6..43e1a4c 100644 --- a/installation/index.html +++ b/installation/index.html @@ -5,13 +5,13 @@ Installation | Boxed - +

Installation

Prerequisites

Even though you can use Boxed without TypeScript and still leverage some benefits, we recommend to use it to get all of the benefits Boxed can provide.

Installation

In your console

$ yarn add @swan-io/boxed

or

$ npm install @swan-io/boxed
- + \ No newline at end of file diff --git a/lazy/index.html b/lazy/index.html index 4a6e623..1f7f408 100644 --- a/lazy/index.html +++ b/lazy/index.html @@ -5,13 +5,13 @@ Lazy | Boxed - +

Lazy

Lazy(f)

Creates a lazy value. The computation won't happen until the first access.

A lazy type exposes a get method that'll return the result from the computation.

import { Lazy } from "@swan-io/boxed";

const lazy = Lazy(() => {
return myComputation();
});

lazy.get()

Computes the value once and returns it.

lazy.get(); // value is computed and return here
- + \ No newline at end of file diff --git a/nested-optional-values/index.html b/nested-optional-values/index.html index bef6869..5432dc3 100644 --- a/nested-optional-values/index.html +++ b/nested-optional-values/index.html @@ -5,13 +5,13 @@ Nested optional values | Boxed - +

Nested optional values

Managing optionality with undefined and null can lead to tedious code, especially when dealing with default values.

Let's assume that we have the following values in scope:

declare var input: string | undefined;
declare function parseInput(input: string): Array<string>;
declare function transform(input: Array<string>): Array<string> | undefined;
declare function print(input: Array<string>): string;
declare function prettify(input: string): string;

Here, parse always returns an Array<string>, and transform can return either an Array<string> or undefined.

Handling this using null or undefined values would lead to code like the following:

const parsed = input != undefined ? parseInput(input) : undefined;
// Keep the `parsed` value if `transform` doesn't output
const transformed =
parsed != undefined ? transform(parsed) ?? parsed : undefined;
// Fallback at the end
const printed = transformed != undefined ? print(transformed) : undefined;
const value = printed != undefined ? prettify(printed) : "fallback";

We lose a lot of the code intent, as we're distracted with some unnecessary complexity.

Now, let's tweak our values so that we use the Option type instead of undefined:

declare var input: Option<string>;
declare function parseInput(input: string): Array<string>;
declare function transform(input: Array<string>): Option<Array<string>>;
declare function print(input: Array<string>): string;
declare function prettify(input: string): string;

Using Option, the same code as above can be written as follows:

input
.map(parseInput)
.flatMap(transform)
.map(print)
.map(prettify)
.getOr("fallback");

Here, the intent of the code is clearly represented, making it much easier to follow.

If we need quick interop with existing code returning undefined or null values, Boxed provides transformers:

If we were to assume again that we have:

declare var input: string | undefined;
declare function parseInput(input: string): Array<string>;
declare function transform(input: Array<string>): Array<string> | undefined;
declare function print(input: Array<string>): string;
declare function prettify(input: string): string;

We'd simplfy need to write the following:

Option.fromNullable(input)
.map(parseInput)
.flatMap((input) => Option.fromNullable(transform(input)))
.map(print)
.map(prettify)
.getOr("fallback");
- + \ No newline at end of file diff --git a/option/index.html b/option/index.html index beca52e..56da1bc 100644 --- a/option/index.html +++ b/option/index.html @@ -5,13 +5,13 @@ Option<Value> | Boxed - +
-

Option<Value>

The Option type can be used as a replacement for null and undefined when manipulating optional data. Contrary to null and undefined, an option is kind of like a box, that contains a value or not.

It can be useful to distinguish values between each other: you can represent Some(None) with options, whereas undefined or null replace the value they intend to make optional.

An option can have two possible states:

  • Some(value)
  • None

Create an Option value

To create an option, use the Some and None constructors:

import { Option } from "@swan-io/boxed";

const aName = Option.Some("John");
const bName = Option.None();

// You can enforce the type using a type parameter
Option.Some<string>("John");
Option.None<string>();

You get interop with null and undefined:

// `value` being `null` or `undefined` makes a `None`
const a = Option.fromNullable(value);

// `value` being `null` makes a `None`
const b = Option.fromNull(value);

// `value` being `undefined` makes a `None`
const c = Option.fromUndefined(value);

Methods

The option type provides a few manipulation functions:

.map(f)

Option<A>.map<B>(f: (value: A) => B): Option<B>

If the option is Some(value) returns Some(f(value)), otherwise returns None.

Examples
Option.Some(2).map((x) => x * 2);
// Option.Some<4>

Option.None().map((x) => x * 2);
// Option.None

.flatMap(f)

Option<A>.flatMap<B>(f: (value: A) => Option<B>): Option<B>

If the option is Some(value) returns f(value), otherwise returns None.

Examples
Option.Some(3).flatMap((x) => (x > 2 ? Option.None() : Option.Some(2)));
// Option.None

Option.Some(1).flatMap((x) => (x > 2 ? Option.None() : Option.Some(2)));
// Option.Some<2>

option.flatMap((value) => value.optionalProperty);
// Option<optionalProperty>

.filter(f)

Option<A>.filter(f: (value: A) => boolean): Option<A>

If the option is Some(value) and that f(value) is true, returns Some(value), otherwise returns None.

Examples
Option.Some(3).filter((x) => x > 2);
// Option.Some(3)

Option.Some(1).filter((x) => x > 2);
// Option.None

.getOr(defaultValue)

Alias: getWithDefault

Option<A>.getOr(defaultValue: A): A

If the option is Some(value) returns value, otherwise returns defaultValue.

Examples
Option.Some(2).getOr(1);
// 2

Option.None().getOr(1);
// 1

.get()

Option<A>.get(): A

Returns the value contained in Some(value). Only usable within a isSome() check.

Examples
const value = option.get();
// does not compile

if (option.isSome()) {
const value = option.get();
// value
}

.isSome()

Option<A>.isSome(): boolean

Type guard. Checks if the option is Some(value)

Examples
Option.Some(2).isSome();
// true

Option.None().isSome();
// false

if (option.isSome()) {
const value = option.get();
}

.isNone()

Option<A>.isNone(): boolean

Type guard. Checks if the option is None

Examples
Option.Some(2).isNone();
// false

Option.None().isNone();
// true

.toNull()

Option<A>.toNull(): A | null

Returns null if the option is None, returns the value otherwise

Examples
Option.Some(2).toNull();
// 2

Option.None().toNull();
// null

.toUndefined()

Option<A>.toUndefined(): A | undefined

Returns undefined if the option is None, returns the value otherwise

Examples
Option.Some(2).toUndefined();
// 2

Option.None().toUndefined();
// undefined

.toResult(errorWhenNone)

Option<A>.toResult<E>(valueWhenNone: E): Result<A, E>

Returns Ok if the option is Some, returns Error otherwise

Examples
const a = Option.Some(1).toResult("NotFound");
// Ok<1>

const b = Option.None().toResult("NotFound");
// Error<"NotFound">

.match()

Option<A>.match<B>(config: {
Some: (value: A) => B;
None: () => B;
}): B

Match the option state

Examples
const valueToDisplay = option.match({
Some: (value) => value,
None: () => "No value",
});
// value | "No value"

.tap(func)

Option<A>.tap(func: (option: Option<A>) => unknown): Option<A>

Executes func with option, and returns option. Useful for logging and debugging.

Examples
option.tap(console.log).map((x) => x * 2);

.tapSome(func)

Option<A>.tapSome(func: (option: A) => unknown): Option<A>

Executes func with option's value if Some, and returns option. Useful for logging and debugging.

Examples
option.tapSome(console.log).map((x) => x * 2);

Statics

Option.isOption(value)

isOption(value: unknown): boolean

Type guard, checks if the provided value is an option.

Examples
Option.isOption(Option.Some(1));
// true

Option.isOption([]);
// false

Option.all(options)

all(options: Array<Option<A>>): Option<Array<A>>

Turns an "array of options of value" into a "option of array of value".

Examples
Option.all([Option.Some(1), Option.Some(2), Option.Some(3)]);
// Some([1, 2, 3])

Option.all([Option.None(), Option.Some(2), Option.Some(3)]);
// None

Option.allFromDict(options)

allFromDict(options: Dict<Option<A>>): Option<Dict<A>>

Turns a "dict of options of value" into a "option of dict of value".

Examples
Option.allFromDict({ a: Option.Some(1), b: Option.Some(2), c: Option.Some(3) });
// Some({a: 1, b: 2, c: 3})

Option.allFromDict({ a: Option.None(), b: Option.Some(2), c: Option.Some(3) });
// None

TS Pattern interop

Examples
import { match, P } from "ts-pattern";
import { Option } from "@swan-io/boxed";

match(myOption)
.with(Option.P.Some(P.select()), (value) => console.log(value))
.with(Option.P.None, () => "No value")
.exhaustive();

Cheatsheet

MethodInputFunction inputFunction outputReturned value
mapSome(x)xySome(y)
mapNone()not providednot executedNone()
flatMapSome(x)xSome(y)Some(y)
flatMapSome(x)xNone()None()
flatMapNone()not providednot executedNone()
- +

Option<Value>

The Option type can be used as a replacement for null and undefined when manipulating optional data. Contrary to null and undefined, an option is kind of like a box, that contains a value or not.

It can be useful to distinguish values between each other: you can represent Some(None) with options, whereas undefined or null replace the value they intend to make optional.

An option can have two possible states:

  • Some(value)
  • None

Create an Option value

To create an option, use the Some and None constructors:

import { Option } from "@swan-io/boxed";

const aName = Option.Some("John");
const bName = Option.None();

// You can enforce the type using a type parameter
Option.Some<string>("John");
Option.None<string>();

You get interop with null and undefined:

// `value` being `null` or `undefined` makes a `None`
const a = Option.fromNullable(value);

// `value` being `null` makes a `None`
const b = Option.fromNull(value);

// `value` being `undefined` makes a `None`
const c = Option.fromUndefined(value);
Since v3.0.0

Option values are referentially equal if they contain the same value, meaning that Option.Some(1) === Option.Some(1).

Methods

The option type provides a few manipulation functions:

.map(f)

Option<A>.map<B>(f: (value: A) => B): Option<B>

If the option is Some(value) returns Some(f(value)), otherwise returns None.

Examples
Option.Some(2).map((x) => x * 2);
// Option.Some<4>

Option.None().map((x) => x * 2);
// Option.None

.flatMap(f)

Option<A>.flatMap<B>(f: (value: A) => Option<B>): Option<B>

If the option is Some(value) returns f(value), otherwise returns None.

Examples
Option.Some(3).flatMap((x) => (x > 2 ? Option.None() : Option.Some(2)));
// Option.None

Option.Some(1).flatMap((x) => (x > 2 ? Option.None() : Option.Some(2)));
// Option.Some<2>

option.flatMap((value) => value.optionalProperty);
// Option<optionalProperty>

.filter(f)

Option<A>.filter(f: (value: A) => boolean): Option<A>

If the option is Some(value) and that f(value) is true, returns Some(value), otherwise returns None.

Examples
Option.Some(3).filter((x) => x > 2);
// Option.Some(3)

Option.Some(1).filter((x) => x > 2);
// Option.None

.getOr(defaultValue)

Alias: getWithDefault

Option<A>.getOr(defaultValue: A): A

If the option is Some(value) returns value, otherwise returns defaultValue.

Examples
Option.Some(2).getOr(1);
// 2

Option.None().getOr(1);
// 1

.get()

Option<A>.get(): A

Returns the value contained in Some(value). Only usable within a isSome() check.

Examples
const value = option.get();
// does not compile

if (option.isSome()) {
const value = option.get();
// value
}

.isSome()

Option<A>.isSome(): boolean

Type guard. Checks if the option is Some(value)

Examples
Option.Some(2).isSome();
// true

Option.None().isSome();
// false

if (option.isSome()) {
const value = option.get();
}

.isNone()

Option<A>.isNone(): boolean

Type guard. Checks if the option is None

Examples
Option.Some(2).isNone();
// false

Option.None().isNone();
// true

.toNull()

Option<A>.toNull(): A | null

Returns null if the option is None, returns the value otherwise

Examples
Option.Some(2).toNull();
// 2

Option.None().toNull();
// null

.toUndefined()

Option<A>.toUndefined(): A | undefined

Returns undefined if the option is None, returns the value otherwise

Examples
Option.Some(2).toUndefined();
// 2

Option.None().toUndefined();
// undefined

.toResult(errorWhenNone)

Option<A>.toResult<E>(valueWhenNone: E): Result<A, E>

Returns Ok if the option is Some, returns Error otherwise

Examples
const a = Option.Some(1).toResult("NotFound");
// Ok<1>

const b = Option.None().toResult("NotFound");
// Error<"NotFound">

.match()

Option<A>.match<B>(config: {
Some: (value: A) => B;
None: () => B;
}): B

Match the option state

Examples
const valueToDisplay = option.match({
Some: (value) => value,
None: () => "No value",
});
// value | "No value"

.tap(func)

Option<A>.tap(func: (option: Option<A>) => unknown): Option<A>

Executes func with option, and returns option. Useful for logging and debugging.

Examples
option.tap(console.log).map((x) => x * 2);

.tapSome(func)

Option<A>.tapSome(func: (option: A) => unknown): Option<A>

Executes func with option's value if Some, and returns option. Useful for logging and debugging.

Examples
option.tapSome(console.log).map((x) => x * 2);

Statics

Option.isOption(value)

isOption(value: unknown): boolean

Type guard, checks if the provided value is an option.

Examples
Option.isOption(Option.Some(1));
// true

Option.isOption([]);
// false

Option.all(options)

all(options: Array<Option<A>>): Option<Array<A>>

Turns an "array of options of value" into a "option of array of value".

Examples
Option.all([Option.Some(1), Option.Some(2), Option.Some(3)]);
// Some([1, 2, 3])

Option.all([Option.None(), Option.Some(2), Option.Some(3)]);
// None

Option.allFromDict(options)

allFromDict(options: Dict<Option<A>>): Option<Dict<A>>

Turns a "dict of options of value" into a "option of dict of value".

Examples
Option.allFromDict({ a: Option.Some(1), b: Option.Some(2), c: Option.Some(3) });
// Some({a: 1, b: 2, c: 3})

Option.allFromDict({ a: Option.None(), b: Option.Some(2), c: Option.Some(3) });
// None

TS Pattern interop

Examples
import { match, P } from "ts-pattern";
import { Option } from "@swan-io/boxed";

match(myOption)
.with(Option.P.Some(P.select()), (value) => console.log(value))
.with(Option.P.None, () => "No value")
.exhaustive();

Cheatsheet

MethodInputFunction inputFunction outputReturned value
mapSome(x)xySome(y)
mapNone()not providednot executedNone()
flatMapSome(x)xSome(y)Some(y)
flatMapSome(x)xNone()None()
flatMapNone()not providednot executedNone()
+ \ No newline at end of file diff --git a/react-request/index.html b/react-request/index.html index 3903799..42714e8 100644 --- a/react-request/index.html +++ b/react-request/index.html @@ -5,13 +5,13 @@ React Request | Boxed - +

React Request

The AsyncData type removes the need for manual request modeling.

Instead of having to maintain a state like the following, you can store the AsyncData value directly.

type UserQuery = {
isLoading: boolean;
error: Error;
data: User;
};

The problem with this representation is that it can represent impossible states, and require additional work to make it safe. It will also encourage nested conditions, which decreases code readability.

Here's how we can represent this using the AsyncData type.

import { useState, useEffect } from "react";
import { AsyncData } from "@swan-io/boxed";
import { queryUser, User } from "./api";

type Props = {
userId: string;
};

const UserPage = ({ userId }: Props) => {
// Initially, the request hasn't performed
const [user, setUser] = useState(() => AsyncData.NotAsked<User>());

useEffect(() => {
// Indicate that we started loading
setUser(AsyncData.Loading());
const cancel = queryUser({ userId }, (user) => {
// Then, set the received value
setUser(AsyncData.Done(user));
});
return cancel;
}, [userId]);

// We can then match on the value, in a flat way
return user.match({
NotAsked: () => null,
Loading: () => `Loading`,
Done: (user) => `Hello ${user.name}!`,
});
};
- + \ No newline at end of file diff --git a/result/index.html b/result/index.html index acbba87..3a15fa1 100644 --- a/result/index.html +++ b/result/index.html @@ -5,13 +5,13 @@ Result<Ok, Error> | Boxed - +
-

Result<Ok, Error>

The Result can replace exception flows.

Exceptions can be tricky to handle: there's nothing in the type system that tracks if an error has been handled, which is error prone, and adds to your mental overhead. Result helps as it makes the value hold the success state, making it dead-simple to track with a type-system.

Just like the Option type, the Result type is a box that can have two states:

  • Ok(value)
  • Error(error)

Create a Result value

To create a result, use the Ok and Error constructors:

import { Result } from "@swan-io/boxed";

const ok = Result.Ok(1);

const notOk = Result.Error("something happened");

You can convert an option to a Result:

import { Result, Option } from "@swan-io/boxed";

const a = Result.fromOption(Option.Some(1), "NotFound");
// Ok<1>

const b = Result.fromOption(Option.None(), "NotFound");
// Error<"NotFound">

You get interop with exceptions and promises:

// Let's say you have some function that throws an error
const init = (id: string) => {
if (id.length !== 24) {
throw new Error();
}
return new Client({ id });
};

const result = Result.fromExecution(() => init(id));
// Here, result will either be:
// - Ok(client)
// - Error(error)

// It works with promises too:

const value = await Result.fromPromise(() => fetch("/api"));
// `value` will either be:
// - Ok(res)
// - Error(error)

Methods

The result type provides a few manipulation functions:

.map(f)

Result<A, E>.map<B>(f: (value: A) => B): Result<B, E>

If the result is Ok(value) returns Ok(f(value)), otherwise returns Error(error).

Examples
Result.Ok(2).map((x) => x * 2);
// Result.Ok<4>

Result.Ok(2).map((x) => Result.Ok(x * 2));
// Result.Ok<Result.Ok<4>>

.mapError(f)

Result<A, E>.mapError<F>(f: (value: E) => F): Result<A, F>

If the result is Error(error) returns Error(f(error)), otherwise returns Ok(value).

Examples
Result.Error(2).mapError((x) => x * 2);
// Result.Error<4>

Result.Error(2).mapError((x) => Result.Ok(x * 2));
// Result.Error<Result.Ok<4>>

.flatMap(f)

Result<A, E>.flatMap<B, F>(f: (value: A) => Result<B, F>): Result<B, F | E>

If the result is Ok(value) returns f(value), otherwise returns Error(error).

Examples
Result.Ok(1).flatMap((x) =>
x > 2 ? Result.Error("some error") : Result.Ok(2),
);
// Result.Ok<2>

Result.Ok(3).flatMap((x) =>
x > 2 ? Result.Error("some error") : Result.Ok(2),
);
// Result.Error<"some error">

Result.Error("initial error").flatMap((x) =>
x > 2 ? Result.Error("some error") : Result.Ok(2),
);
// Result.Error<"initial error">

.flatMapError(f)

Result<A, E>.flatMapError<B, F>(f: (value: E) => Result<B, F>): Result<A | B, F>

If the result is Error(error) returns f(error), otherwise returns Ok(value).

Examples
Result.Error(3).flatMapError((x) =>
x > 2 ? Result.Error("some error") : Result.Ok(2),
);
// Result.Error<"some error">

Result.Error(1).flatMapError((x) =>
x > 2 ? Result.Error("some error") : Result.Ok(2),
);
// Result.Ok<2>

Result.Ok("ok").flatMapError((x) =>
x > 2 ? Result.Error("some error") : Result.Ok(2),
);
// Result.Ok<"ok">

.getOr(defaultValue)

Alias: getWithDefault

Result<A, E>.getOr(defaultValue: A): A

If the result is Ok(value) returns value, otherwise returns defaultValue.

Examples
Result.Ok(2).getOr(1);
// 2

Result.Error(2).getOr(1);
// 1

.get()

Result<A, E>.get(): A

Returns the value contained in Ok(value). Only usable within a isOk() check.

Examples
const value = result.get();
// does not compile

if (result.isOk()) {
const value = result.get();
// value
}

.getError()

Result<A, E>.getError(): E

Returns the error contained in Error(error). Only usable within a isError() check.

Examples
const error = result.getError();
// does not compile

if (result.isError()) {
const error = result.getError();
// error
}

.isOk()

Result<A, E>.isOk(): boolean

Type guard. Checks if the result is Ok(value)

Examples
Result.Ok(2).isOk();
// true

Result.Error(2).isOk();
// false

if (result.isOk()) {
const value = result.get();
}

.isError()

Result<A, E>.isError(): boolean

Type guard. Checks if the result is Error(error)

Examples
Result.Ok(2).isError();
// false

Result.Error().isError();
// true

if (result.isError()) {
const value = result.getError();
}

.toOption()

Result<A, E>.toOption(): Option<A>

If the result is Ok(value) returns Some(value), otherwise returns None.

Examples
Result.Ok(2).toOption();
// Option.Some<2>

Result.Error(2).toOption();
// Option.None

.match()

Result<A, E>.match<B>(config: {
Ok: (value: A) => B;
Error: (error: E) => B;
}): B

Match the result state

Examples
const valueToDisplay = result.match({
Ok: (value) => value,
Error: (error) => {
console.error(error);
return "fallback";
},
});

.tap(func)

Result<A, E>.tap(func: (result: Result<A, E>) => unknown): Result<A, E>

Executes func with result, and returns result. Useful for logging and debugging.

Examples
result.tap(console.log).map((x) => x * 2);

.tapOk(func)

Result<A, E>.tapOk(func: (value: A) => unknown): Result<A, E>

Executes func with ok, and returns result. Useful for logging and debugging. No-op if result is an error.

Examples
result.tapOk(console.log).map((x) => x * 2);

.tapError(func)

Result<A, E>.tapError(func: (error: E) => unknown): Result<A, E>

Executes func with error, and returns result. Useful for logging and debugging. No-op if result is ok.

Examples
result.tapError(console.log).map((x) => x * 2);

Statics

Result.isResult(value)

isResult(value: unknown): boolean

Type guard, checks if the provided value is a result.

Examples
Result.isResult(Result.Ok(1));
// true

Result.isResult([]);
// false

Result.all(results)

all(options: Array<Result<A, E>>): Result<Array<A>, E>

Turns an "array of results of value" into a "result of array of value".

Examples
Result.all([Result.Ok(1), Result.Ok(2), Result.Ok(3)]);
// Result.Ok<[1, 2, 3]>

Result.all([Result.Error("error"), Result.Ok(2), Result.Ok(3)]);
// Result.Error<"error">

Result.allFromDict(results)

allFromDict(options: Dict<Result<A, E>>): Result<Dict<A>, E>

Turns a "dict of results of value" into a "result of dict of value".

Examples
Result.allFromDict({ a: Result.Ok(1), b: Result.Ok(2), c: Result.Ok(3) });
// Result.Ok<{a: 1, b: 2, c: 3}>

Result.allFromDict({
a: Result.Error("error"),
b: Result.Ok(2),
c: Result.Ok(3),
});
// Result.Error<"error">

Result.fromExecution(() => value)

fromExecution<A, E>(func: () => A) => Result<A, E>

Takes a function returning Value that can throw an Error and returns a Result<Value, Error>

Examples
Result.fromExecution(() => 1);
// Result.Ok<1>

Result.fromExecution(() => {
throw "Something went wrong";
});
// Result.Error<"Something went wrong">

Result.fromPromise(promise)

fromPromise<A, E>(promise: Promise<A>) => Promise<Result<A, E>>

Takes a Promise<Value> that can fail with Error and returns a Promise<Result<Value, Error>>

Examples
await Result.fromPromise(Promise.resolve(1));
// Result.Ok<1>

await Result.fromPromise(Promise.reject(1));
// Result.Error<1>

Result.fromOption(option, valueIfNone)

fromOption<A, E>(option: Option<A>, valueWhenNone: E): Result<A, E>

Takes a function returning Value that can throw an Error and returns a Result<Value, Error>

Examples
const a = Result.fromOption(Option.Some(1), "NotFound");
// Result.Ok<1>

const b = Result.fromOption(Option.None(), "NotFound");
// Result.Error<"NotFound">

TS Pattern interop

import { match, P } from "ts-pattern";
import { Result } from "@swan-io/boxed";

match(myResult)
.with(Result.P.Ok(P.select()), (value) => console.log(value))
.with(Result.P.Error(P.select()), (error) => {
console.error(error);
return "fallback";
})
.exhaustive();

Cheatsheet

MethodInputFunction inputFunction outputReturned value
mapOk(x)xyOk(y)
mapError(e)not providednot executedError(e)
mapErrorOk(x)not providednot executedOk(x)
mapErrorError(e)efError(f)
flatMapOk(x)xOk(y)Ok(y)
flatMapOk(x)xError(f)Error(f)
flatMapError(e)not providednot executedError(e)
flatMapErrorOk(x)not providednot executedOk(x)
flatMapErrorError(e)eOk(y)Ok(y)
flatMapErrorError(e)eError(f)Error(f)
- +

Result<Ok, Error>

The Result can replace exception flows.

Exceptions can be tricky to handle: there's nothing in the type system that tracks if an error has been handled, which is error prone, and adds to your mental overhead. Result helps as it makes the value hold the success state, making it dead-simple to track with a type-system.

Just like the Option type, the Result type is a box that can have two states:

  • Ok(value)
  • Error(error)

Create a Result value

To create a result, use the Ok and Error constructors:

import { Result } from "@swan-io/boxed";

const ok = Result.Ok(1);

const notOk = Result.Error("something happened");

You can convert an option to a Result:

import { Result, Option } from "@swan-io/boxed";

const a = Result.fromOption(Option.Some(1), "NotFound");
// Ok<1>

const b = Result.fromOption(Option.None(), "NotFound");
// Error<"NotFound">

You get interop with exceptions and promises:

// Let's say you have some function that throws an error
const init = (id: string) => {
if (id.length !== 24) {
throw new Error();
}
return new Client({ id });
};

const result = Result.fromExecution(() => init(id));
// Here, result will either be:
// - Ok(client)
// - Error(error)

// It works with promises too:

const value = await Result.fromPromise(() => fetch("/api"));
// `value` will either be:
// - Ok(res)
// - Error(error)
Since v3.0.0

Result values are referentially equal if they contain the same value, meaning that Result.Ok(1) === Result.Ok(1) and Result.Error(1) === Result.Error(1).

Methods

The result type provides a few manipulation functions:

.map(f)

Result<A, E>.map<B>(f: (value: A) => B): Result<B, E>

If the result is Ok(value) returns Ok(f(value)), otherwise returns Error(error).

Examples
Result.Ok(2).map((x) => x * 2);
// Result.Ok<4>

Result.Ok(2).map((x) => Result.Ok(x * 2));
// Result.Ok<Result.Ok<4>>

.mapError(f)

Result<A, E>.mapError<F>(f: (value: E) => F): Result<A, F>

If the result is Error(error) returns Error(f(error)), otherwise returns Ok(value).

Examples
Result.Error(2).mapError((x) => x * 2);
// Result.Error<4>

Result.Error(2).mapError((x) => Result.Ok(x * 2));
// Result.Error<Result.Ok<4>>

.flatMap(f)

Result<A, E>.flatMap<B, F>(f: (value: A) => Result<B, F>): Result<B, F | E>

If the result is Ok(value) returns f(value), otherwise returns Error(error).

Examples
Result.Ok(1).flatMap((x) =>
x > 2 ? Result.Error("some error") : Result.Ok(2),
);
// Result.Ok<2>

Result.Ok(3).flatMap((x) =>
x > 2 ? Result.Error("some error") : Result.Ok(2),
);
// Result.Error<"some error">

Result.Error("initial error").flatMap((x) =>
x > 2 ? Result.Error("some error") : Result.Ok(2),
);
// Result.Error<"initial error">

.flatMapError(f)

Result<A, E>.flatMapError<B, F>(f: (value: E) => Result<B, F>): Result<A | B, F>

If the result is Error(error) returns f(error), otherwise returns Ok(value).

Examples
Result.Error(3).flatMapError((x) =>
x > 2 ? Result.Error("some error") : Result.Ok(2),
);
// Result.Error<"some error">

Result.Error(1).flatMapError((x) =>
x > 2 ? Result.Error("some error") : Result.Ok(2),
);
// Result.Ok<2>

Result.Ok("ok").flatMapError((x) =>
x > 2 ? Result.Error("some error") : Result.Ok(2),
);
// Result.Ok<"ok">

.getOr(defaultValue)

Alias: getWithDefault

Result<A, E>.getOr(defaultValue: A): A

If the result is Ok(value) returns value, otherwise returns defaultValue.

Examples
Result.Ok(2).getOr(1);
// 2

Result.Error(2).getOr(1);
// 1

.get()

Result<A, E>.get(): A

Returns the value contained in Ok(value). Only usable within a isOk() check.

Examples
const value = result.get();
// does not compile

if (result.isOk()) {
const value = result.get();
// value
}

.getError()

Result<A, E>.getError(): E

Returns the error contained in Error(error). Only usable within a isError() check.

Examples
const error = result.getError();
// does not compile

if (result.isError()) {
const error = result.getError();
// error
}

.isOk()

Result<A, E>.isOk(): boolean

Type guard. Checks if the result is Ok(value)

Examples
Result.Ok(2).isOk();
// true

Result.Error(2).isOk();
// false

if (result.isOk()) {
const value = result.get();
}

.isError()

Result<A, E>.isError(): boolean

Type guard. Checks if the result is Error(error)

Examples
Result.Ok(2).isError();
// false

Result.Error().isError();
// true

if (result.isError()) {
const value = result.getError();
}

.toOption()

Result<A, E>.toOption(): Option<A>

If the result is Ok(value) returns Some(value), otherwise returns None.

Examples
Result.Ok(2).toOption();
// Option.Some<2>

Result.Error(2).toOption();
// Option.None

.match()

Result<A, E>.match<B>(config: {
Ok: (value: A) => B;
Error: (error: E) => B;
}): B

Match the result state

Examples
const valueToDisplay = result.match({
Ok: (value) => value,
Error: (error) => {
console.error(error);
return "fallback";
},
});

.tap(func)

Result<A, E>.tap(func: (result: Result<A, E>) => unknown): Result<A, E>

Executes func with result, and returns result. Useful for logging and debugging.

Examples
result.tap(console.log).map((x) => x * 2);

.tapOk(func)

Result<A, E>.tapOk(func: (value: A) => unknown): Result<A, E>

Executes func with ok, and returns result. Useful for logging and debugging. No-op if result is an error.

Examples
result.tapOk(console.log).map((x) => x * 2);

.tapError(func)

Result<A, E>.tapError(func: (error: E) => unknown): Result<A, E>

Executes func with error, and returns result. Useful for logging and debugging. No-op if result is ok.

Examples
result.tapError(console.log).map((x) => x * 2);

Statics

Result.isResult(value)

isResult(value: unknown): boolean

Type guard, checks if the provided value is a result.

Examples
Result.isResult(Result.Ok(1));
// true

Result.isResult([]);
// false

Result.all(results)

all(options: Array<Result<A, E>>): Result<Array<A>, E>

Turns an "array of results of value" into a "result of array of value".

Examples
Result.all([Result.Ok(1), Result.Ok(2), Result.Ok(3)]);
// Result.Ok<[1, 2, 3]>

Result.all([Result.Error("error"), Result.Ok(2), Result.Ok(3)]);
// Result.Error<"error">

Result.allFromDict(results)

allFromDict(options: Dict<Result<A, E>>): Result<Dict<A>, E>

Turns a "dict of results of value" into a "result of dict of value".

Examples
Result.allFromDict({ a: Result.Ok(1), b: Result.Ok(2), c: Result.Ok(3) });
// Result.Ok<{a: 1, b: 2, c: 3}>

Result.allFromDict({
a: Result.Error("error"),
b: Result.Ok(2),
c: Result.Ok(3),
});
// Result.Error<"error">

Result.fromExecution(() => value)

fromExecution<A, E>(func: () => A) => Result<A, E>

Takes a function returning Value that can throw an Error and returns a Result<Value, Error>

Examples
Result.fromExecution(() => 1);
// Result.Ok<1>

Result.fromExecution(() => {
throw "Something went wrong";
});
// Result.Error<"Something went wrong">

Result.fromPromise(promise)

fromPromise<A, E>(promise: Promise<A>) => Promise<Result<A, E>>

Takes a Promise<Value> that can fail with Error and returns a Promise<Result<Value, Error>>

Examples
await Result.fromPromise(Promise.resolve(1));
// Result.Ok<1>

await Result.fromPromise(Promise.reject(1));
// Result.Error<1>

Result.fromOption(option, valueIfNone)

fromOption<A, E>(option: Option<A>, valueWhenNone: E): Result<A, E>

Takes a function returning Value that can throw an Error and returns a Result<Value, Error>

Examples
const a = Result.fromOption(Option.Some(1), "NotFound");
// Result.Ok<1>

const b = Result.fromOption(Option.None(), "NotFound");
// Result.Error<"NotFound">

TS Pattern interop

import { match, P } from "ts-pattern";
import { Result } from "@swan-io/boxed";

match(myResult)
.with(Result.P.Ok(P.select()), (value) => console.log(value))
.with(Result.P.Error(P.select()), (error) => {
console.error(error);
return "fallback";
})
.exhaustive();

Cheatsheet

MethodInputFunction inputFunction outputReturned value
mapOk(x)xyOk(y)
mapError(e)not providednot executedError(e)
mapErrorOk(x)not providednot executedOk(x)
mapErrorError(e)efError(f)
flatMapOk(x)xOk(y)Ok(y)
flatMapOk(x)xError(f)Error(f)
flatMapError(e)not providednot executedError(e)
flatMapErrorOk(x)not providednot executedOk(x)
flatMapErrorError(e)eOk(y)Ok(y)
flatMapErrorError(e)eError(f)Error(f)
+ \ No newline at end of file diff --git a/search/index.html b/search/index.html index 6add24e..1e92979 100644 --- a/search/index.html +++ b/search/index.html @@ -5,13 +5,13 @@ Search the documentation | Boxed - +

Search the documentation

- + \ No newline at end of file diff --git a/serializer/index.html b/serializer/index.html index ead72c0..82f5a5e 100644 --- a/serializer/index.html +++ b/serializer/index.html @@ -5,13 +5,13 @@ Serializer | Boxed - +

Serializer

The serializer enables you to serialize some Boxed values (e.g. to store in LocalStorage, or to hydrate data from SSR).

import { Serializer } from "@swan-io/boxed";

Serializer.encode(value)

Stringifies the input to JSON, managing the AsyncData, Option and Result types properly.

Serializer.encode({
data: AsyncData.Done({
name: Option.None(),
}),
});
// {"data":{"__boxed_type__":"AsyncData","tag":"Done","value":{"name":{"__boxed_type__":"Option","tag":"None"}}}}

Serializer.decode(value)

Parse the JSON input, reviving the AsyncData, Option and Result types properly.

Serializer.decode(`{"__boxed_type__":"Option","tag":"None"}`); // Option.None();
- + \ No newline at end of file diff --git a/video/ide.m4v b/video/ide.m4v new file mode 100644 index 0000000..e7003d2 Binary files /dev/null and b/video/ide.m4v differ diff --git a/video/ide.mov b/video/ide.mov deleted file mode 100644 index f2fb3ba..0000000 Binary files a/video/ide.mov and /dev/null differ