Coder Social home page Coder Social logo

swan-io / boxed Goto Github PK

View Code? Open in Web Editor NEW
663.0 663.0 20.0 43.77 MB

Essential building-blocks for functional & safe TypeScript code

Home Page: https://swan-io.github.io/boxed

License: MIT License

TypeScript 94.86% JavaScript 5.14%
adt fp functional future monads option result typescript

boxed's People

Contributors

0xcafeadd1c7 avatar ahaoboy avatar bloodyowl avatar blowery avatar bollaberg avatar christianivicevic avatar ghoullier avatar kyleshevlin avatar longdog avatar madx avatar runeh avatar zoontek avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

boxed's Issues

TypeScript errors when invoking `Result.get()`

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.

`Result.get()` can't accept outside `Result.isOk` scope even if `Result.isError()` checked and return before.

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 :

image

Add Option.filter and Result.filter

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.

Handling more granular variants of fetch states with AsyncData or similar

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!

Result from serialized json

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?

Add an empty Result.Ok()

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

Does this support deserialization for APIs?

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>;
  ...
}

Package fails to install due to missing yarn dependency

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

"prepare": "yarn test && yarn build",

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

Would it be weird to have a new constructor method on Option to create an option from truthy?

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

ts-pattern matching tuple where one value is an option is not working

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

Why do you have a peerDependency on typescript?

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...

Infer `Ok<A,E>[]` from a `Result<A,E>[]`?

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?

ts-pattern interop with `Option<boolean>` is not exhaustive

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 👍

How to detect is a Future or Result is instanceof Future or Result

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

`Result` parsing from interface compatible object

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 🤫

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.