swan-io / boxed Goto Github PK
View Code? Open in Web Editor NEWEssential building-blocks for functional & safe TypeScript code
Home Page: https://swan-io.github.io/boxed
License: MIT License
Essential building-blocks for functional & safe TypeScript code
Home Page: https://swan-io.github.io/boxed
License: MIT License
When trying to access Result
's value using get
method after isOk
type guard like so
function randomNumber(): Result<number, string> {
if (Math.random() > 0.5) {
return Result.Ok(Math.random())
} else {
return Result.Error('Cannot create random number')
}
}
const result = randomNumber();
if (result.isOk()) {
const value = result.get()
}
TypeScript complains about result.get()
with the following output
The 'this' context of type 'Result<number, string> & { tag: "Ok"; value: { value: number; }; }' is not assignable to method's 'this' of type 'Result<number, string> & { value: { tag: "Ok"; value: number; }; }'.
Type 'Result<number, string> & { tag: "Ok"; value: { value: number; }; }' is not assignable to type '{ value: { tag: "Ok"; value: number; }; }'.
Types of property 'value' are incompatible.
Type '({ tag: "Ok"; value: number; } | { tag: "Error"; value: string; }) & { value: number; }' is not assignable to type '{ tag: "Ok"; value: number; }'.
Type '{ tag: "Error"; value: string; } & { value: number; }' is not assignable to type '{ tag: "Ok"; value: number; }'.
Types of property 'tag' are incompatible.
Type '"Error"' is not assignable to type '"Ok"'.
I am on TypeScript version of 4.6.3
.
const a = async () => {
const createAcc = await Result.fromPromise(
auth.createUser({
displayName: name,
email,
emailVerified: false,
password
})
);
if (createAcc.isError()) return createAcc
// we should allow get value here because we catched error case and return before.
const acc = createAcc.get()
}
Hi, i think this case is common, in functions, return error catched before get final result give cleaner code, if only check isOk()
with a scope we will end up many nested if
.
For now error show like this :
Hey,
thanks for the nice streamlined library!
What I am missing from the API that exists in fp-ts
is a filter
function for Option
and Result
.
Here is the reference in fp-ts
.
I implemented it in user-land like this for now:
export function OptionFilter<T>(
option: Option<T>,
filterFn: (param: T) => boolean
): Option<T> {
return option.flatMap((value) =>
filterFn(value) ? Option.Some(value) : Option.None()
);
}
It would be nice to have this integrated in the library so we can use chaining.
Hello!
I have a question rather than an issue. Hopefully not in the wrong forum, if so do tell and I will correct.
Library looks awesome. I've just recently been experimenting with something similar to AsyncData
with its property .match(...)
but I've been using ts-pattern
for this instead.
My case is that I want to 'patch' the query result from a basic react-query/useQuery
. This result and the patching does however lead me into another possible state, Refreshing<TData>
.
My question is if there are any examples of how to model this with this library, or if someone could point me in the right direction.
Thanks!
Hi! I watched your talk on nordicjs, it was inspiring. I've been using it for some of my vue projects.
I'm currently using it both on the server and client through tRPC. When tRPC serializes the result, which from what I've understood is a class with specific methods, it creates a json struct which it sends down to the client.
Therefore, I would need to turn something like {"tag": "Ok", "value: "[...]"}
into a Result
class on the client, if that makes sense. Do you think there's a valid use case for this?
It would be useful, in some cases, to be able to return Result.Ok() without passing any params.
Do you think it is possible?
Thanks
I'd like to use these Option types in an API, IE currently an interface might be:
interface Person {
name: string;
display_name?: string // I'd like this to be display_name: Option<string>;
...
}
is there such a thing as an async map? something that would, instead of returning Result<Promise<true>>
to Promise<Result<true>>
?
So that it would be obvious?
I do not know, if it is possible but would be great!
thanks,
I plan use boxed in my cli project, but I find it lack this feature, so I turn to use fp-ts instead.
When trying to add this package to my project with pnpm, I am getting a yarn not found exception. This is because the prepare script is being ran on my machine after install
ERR_PNPM_PREPARE_PKG_FAILURE not found: yarn
Line 38 in e951a02
According to npm docs, the npm prepare
script gets ran before publish but also after installing the package. If the purpose of the prepare script is to test & build the source files, maybe they can be added to prepublishOnly
instead?
Reference: https://docs.npmjs.com/cli/v8/using-npm/scripts#npm-install
Don't know if this is something smart, but would it be interesting to have an API like this:
Option.fromTruthy('') // Option<string> // effectively would be a None
Option.fromTruthy(0) // Option<number> // effectively would be a None
Option.fromTruthy(-0) // Option<number> // effectively would be a None
Option.fromTruthy(undefined) // Option<undefined> // effectively would be a None
Option.fromTruthy(null) // Option<null> // effectively would be a None
Option.fromTruthy(NaN) // Option<number> // effectively would be a None
If theres interest I can make a PR and test it as well
Hey and thanks for a great library.
I just stumbled across what I think is a bug. I'm not 100% sure if this belongs here or at ts-pattern's repo but my repro suggests that it could be here (if I'm not entirely mistaken and the error is on my end.. :)
I want to pattern match on a tuple where one of the values is an Option
. Below is an example that highlights the problem:
const withOption = match(Option.Some("foo")).with(
Option.P.Some(P.select()),
(val) => val // val: string
);
const withoutOptionTuple = match([{ type: "a" }, { type: "baz" }]).with(
[{ type: "a" }, { type: "baz" }],
([a, b]) => [a, b] // a: { type: string }, b: { type: string }
);
const withOptionTuple = match([{ type: "a" }, Option.Some("bar")]).with(
[{ type: "a" }, Option.P.Some(P.select())],
([a, b]) => [a, b] // a: string, b: string
);
The last example says the parameter to the callback function is a string even if I don't destructure:
const withOptionTuple = match([{ type: "a" }, Option.Some("bar")]).with(
[{ type: "a" }, Option.P.Some(P.select())],
(val) => val // val: string
);
Which kind of leads me to believe this might be a TS issue unrelated to your library.. but idk 🤷
Do you see anything weird here too?
sandbox: https://codesandbox.io/s/late-haze-4rt5pg?file=/src/index.ts
Howdy,
With 5.x, you've added a peerDependency
on typescript > 5
. That forces anyone who uses swan with yarn to declare a full dependency (not a devDependency) on typescript in projects that use boxed. Was that intentional? Do you really require typescript as a peer dependency at runtime?
Poking through the codebase, I don't see any imports from typescript in the src folder, so it doesn't seem so. Is this primarily to communicate that boxed requires at least typescript 5?
Would you consider changing it to a devDependency
for boxed? Sadly there's no way to declare a peer-dev-dependency...
I have a bunch of results. Now, if any of these errors. I want do this:
export const getOrganizationUsers = async (
org_id: string,
client: Client,
) => {
const result = await getAllOrganizationUsers(org_id,);
const work = result.map(async (row) => {
return await client.getUserProfile(row.user_id);
});
const data = await Promise.all(work);
/* const data = Result<{
id: string;
first_name: string;
last_name: string;
email: string;
created_on: string;
}, Error>[]
*/
if (data.some((d) => d.isError())) {
return Result.Error(new Error("Unable to get all users in organization"));
}
data;
};
Because typescript is being typescript, the if does not help the compiler to turn the last row(data;
) into OK<A, E>
. It still thinks its Result
.
As you do, you try to add a type guard:
import { Result, Ok } from "@swan-io/boxed";
export const listOfResultsHasError = <T, E>(
results: Result<T, E>[],
): results is Ok<T, E> => {
return results.some((r) => r.isError());
};
But Ok
is not an exported class :(
Any advice on what to do here?
I'm trying to match on the contents of an Option<boolean>
with patterns for Some<true>
, Some<false>
and None
, but ts-pattern gives a NonExhaustiveError
.
Code below, and in this sandbox: https://codesandbox.io/s/vigilant-rhodes-w5mgy4?file=/src/index.ts
import { Option } from '@swan-io/boxed';
import { match } from 'ts-pattern';
const exists: Option<boolean> = Option.Some(true);
const booleanMatch = match(exists)
.with(Option.P.Some(true), () => 'yes')
.with(Option.P.Some(false), () => 'no')
.with(Option.P.None, () => 'maybe')
// error here: NonExhaustiveError<Some<boolean>>
.exhaustive();
Not sure if this is expected to work or not, I might be confused about the matching of Some
objects. . It does work as expected if I use an Option<'yes' | 'no'>
, so seems to be something about boolean not being narrowed? Could be in ts-pattern, but I thought I'd start here.
Apart from this, really enjoying boxed 👍
Hi,
thanks for building this library, so far it has been my team's life much easier!
For performance tracking I have to find out, how long it takes for a function to execute. That part is not the problem, unfortunately our code is a mixture of return values, and tracking Future
, Result
and Promise
requires type-specific code.
It looks like using instanceof
cannot be used here, as can be seen in this Replit. So I created these type guards, which while serviceable are rather hacky:
type AnyFunction = (...args: any[]) => any;
const isFuture = (x: unknown): x is Future<any> =>
Reflect.has((x as AnyFunction).constructor, "value");
const isResult = (x: unknown): x is Result<any, any> =>
Reflect.has((x as AnyFunction).constructor, "isOk");
Is there a better solution for this?
Thank you for any help or tips and I hope you have a great day!
Robert
Hello,
I've been using boxed for API result validation using BlitzJS and I'm using a small helper as the class is obviously not transferred from backend to frontend through the "magic" link they introduce based on react queries.
Therefore to be able to access Result
methods, I need to recreate them.
To do so, I use:
import { Result } from "@swan-io/boxed"
// util for result from swan-io/boxed
export function dictToResult<T1, T2>(
d: { tag: "Ok"; value: T1 } | { tag: "Error"; error: T2 },
): Result<T1, T2> {
if (d.tag === "Ok") {
return Result.Ok(d.value)
} else {
return Result.Error(d.error)
}
}
It might not be optimal (I'm not a Typescript expert) but it works.
I guess it could be useful to have a similar function added as static function to the Result
object (like the typeguards etc) to make is accessible to more lib users.
It might be also safer for me as I bumped from 1.X to 2.X and saw that it moved the value
to error
field 🤫
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.