0.2.3 • Published 3 months ago

utils-toolkit v0.2.3

Weekly downloads
-
License
ISC
Repository
-
Last release
3 months ago

Utils Toolkit

For your ease of development, with a heavy emphasis on well-typed, frustration-free DX.

Installation

npm

npm i utils-toolkit

yarn

yarn add utils-toolkit

Usage

Return Types

Result<T, E>

For the Result type, we use the convention of Ok and Err to represent the two possible outcomes of a function. This is similar to Rust's Result type, albeit it's hard to replicate the exact same behavior and usage pattern in Javascript.

As the implementer, you are responsible for returning the correct type. For example, if you have a function that returns a Result<string, Error>, you must return either Ok<string>('some value') to indicate a success (it doesn't have to be a string, it can be any type) or Err<Error>(new Error('some error')) to indicate a failure (again, it doesn't have to be a generic Error, it can be any custom error you design as long as it implements Javascript's Error).

import { Result } from "utils-toolkit";

function foo(ok: boolean): Result<string, Error> {
  if (!ok) {
    return Result<string, Error>(new Error("Something went wrong"));
  }

  return Result<string, Error>("Everything is fine");
}

As the user, you can use the ok property to check the type of the result. If you want to get the value of the result, you can use the unwrap() method. If you want to be forceful, you can use the unwrapErr() method to get the value of the result or throw an error if the result is an Err.

const okResult: Result<string, Error> = foo(true);

console.log(okResult.ok); // true
console.log(okResult.unwrap()); // 'Everything is fine'
const errResult = foo(false);

console.log(errResult.ok); // false
console.log(errResult.unwrap()); // Error { message: 'Something went wrong' }
if (!errResult.ok) {
  //                  ✨ type inference magic -- errResult is now an Err<Error> variant
  const error: Error = errResult.unwrap();

  console.log(error.message); // 'Something went wrong'
}
// ⚠️ or if you want to be forceful

console.log(okResult.unwrapErr()); // 'Everything is fine'
console.log(errResult.unwrapErr()); // throws 'Error: Something went wrong'

Option<T>

For the Option type, we use the convention of Some and None to represent the two possible outcomes of a function. This is similar to Rust's Option type.

As the implementer, you only need to wrap your return value in the Option 'constructor' and the library will take care of the rest. For example, if you have a function that fetches external data and you're not sure if it'll return a non-nullish value, you can wrap it in the Option 'constructor' to indicate that the function may return null or undefined.

import { Option } from "utils-toolkit";

function bar(some: boolean): Option<string> {
  if (!some) {
    return Option<string>(null);
  }

  return Option("Everything is fine");
}

As the user, you can use the some property to check the type of the result. If you want to get the value of the result, you can use the coalesce() method. Optionally (haha get it?), you can pass in a default value to the coalesce() method to return the default value if the result is None.

const someResult: Option<string> = foo(true);

console.log(someResult.some); // true
console.log(someResult.coalesce()); // 'Everything is fine'
console.log(someResult.coalesce("Default value")); // 'Everything is fine' <-- default value is ignored
const noneOption: Option<string> = bar(false);

console.log(noneOption.some); // false
console.log(noneOption.coalesce()); // null
console.log(noneOption.coalesce("Default value")); // 'Default value'

Functions - Server

ApiResponse<T>

The ApiResponse 'constructor' is a helper function that returns a standardized response body for your API. It takes in a generic type T and returns an object with a success boolean value and a data property of type T if the response is successful, or an error property of type Error if the response is errored.

import { ApiResponse } from "utils-toolkit";

const routeHandler = async (req: Request, res: Response) => {
  const apiRes = await fetch("https://some-api.com");

  if (!apiRes.ok) {
    return res.json(ApiResponse<Data>(new Error("Something went wrong")));
  }

  const data = await apiRes.json();
  return res.json(ApiResponse<Data>(data));
};
// Successful response body
{
  "success": true,
  "data": {
    "some": "data"
  }
}
// Errored response body
{
  "success": false,
  "error": {
    "message": "Something went wrong"
  }
}

Functions - Client

consumeApiResponse<T>

The consumeApiResponse function is a helper function that takes in an ApiResponse object and returns a Result<T, Error> object. If the ApiResponse object is successful, it returns an Ok<T> variant with the data property of the ApiResponse object. If the ApiResponse object is errored, it returns an Err<Error> variant with the error property of the ApiResponse object.

import { consumeApiResponse } from "utils-toolkit";
// ... other imports

const Component = () => {
  const [data, setData] = useState<Data>(null);
  const [error, setError] = useState<Error>(null);

  useEffect(() => {
    const fetchData = async () => {
      const res = await fetch("https://my-api.com");
      const resData = await res.json();

      const data = consumeApiResponse<Data>(resData);

      if (!data.ok) {
        // const error = data.unwrapErr(); <-- if you want to be forceful and risk crashing your page

        const error = data.unwrap();
        return setError(error.message);
      }

      const myData = data.unwrap();
      // do something with myData
      setData(myData);
    };

    fetchData();
  }, []);

  return (
    <>
      {data && <div>{data}</div>}
      {error && <div>{error}</div>}
    </>
  );
};

cn

First popularized by the rapidly growing shadcn/ui library, the cn function is a helper function that takes in any number of args of type ClassValues and and returns a single string of class names. This is useful for conditionally applying CSS classes to your React components.

Note that it is only meant for TailwindCSS classes, so it will not work with other CSS-in-JS libraries like styled-components or emotion. Also, it makes use of the clsx and tailwind-merge libraries, which I obviously did not write. If they are updated and break this function, I will do my best to maintain it, but just know that it is not guaranteed to work with future versions of those libraries.

I also have no idea how to write unit tests for this function, so if you have any ideas or suggestions, please let me know! Otherwise, this function will remain untested, so use at your own risk.

import { cn } from "utils-toolkit";

const Component = ({ someProps, darkMode }) => {
  return (
    <div
      className={cn("text-black", {
        "text-white": darkMode,
      })}
    >
      Hello world!
    </div>
  );
};

The above snippet will produce the following if darkMode is false (assumed default here):

<div class="text-black">Hello world!</div>

And the following if darkMode is true:

<div class="text-white">Hello world!</div>

Functions - Miscellaneous

isNullish

import { isNullish } from "utils-toolkit";

console.log(isNullish("Some value here")); // false
console.log(isNullish(123)); // false
console.log(isNullish(true)); // false
console.log(isNullish([])); // false
console.log(isNullish({})); // false
console.log(isNullish(null)); // true
console.log(isNullish(undefined)); // true
console.log(isNullish("")); // false
console.log(isNullish(0)); // false
console.log(isNullish(false)); // false

More to come

0.2.3

3 months ago

0.2.2

8 months ago

0.2.1

8 months ago

0.2.0

8 months ago

0.1.2

8 months ago

0.1.1

8 months ago

0.1.0

8 months ago