Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

ts-error-as-value

Package Overview
Dependencies
Maintainers
1
Versions
38
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ts-error-as-value - npm Package Compare versions

Comparing version 0.1.8 to 0.1.9

.idea/jsLibraryMappings.xml

3

.eslintrc.json

@@ -10,3 +10,4 @@ {

"project": [
"./tsconfig.json"
"./tsconfig.json",
"./tsconfig.jest.json"
]

@@ -13,0 +14,0 @@ }

{
"name": "ts-error-as-value",
"version": "0.1.8",
"version": "0.1.9",
"description": "Errors as values in typescript",

@@ -5,0 +5,0 @@ "main": "lib/index.js",

### Typescript error as value
### Motivation
At [RollCredits](https://www.rollcredits.io/) we have all come to the conclusion that try / catch causes more headaches than it's worth, and wanted a good error as values system to use in its place.
Any error-as-values library in typescript is liable for being used *a lot* within any project which adds it, so making the API as convenient as humanly possible was my primary concern.
Any error-as-values library in typescript is liable for being used *a lot* within any project which adds it, so making the API as convenient as humanly possible was our primary concern.
The alternative typescript libraries for achieving errors-as-values all seem to have verbose and cumbersome APIs, often wrapping all returns with class instances, and asking you to call method on them such as "isOk" or "isErr" to function.
Instead of this, we leverage typescript's discriminated unions to handle most of the heavy lifting for us. This greatly (in our opinion) reduces how cumbersome the API is to use.
Instead of this, we leverage typescript's discriminated unions to handle most of the heavy lifting for us.
To further decrease friction for using this in your project, We have also included the ability to import the functions and types of this package into your project's global scope.
To further decrease friction for using this in your project, you can also import the functions and types of this package into your project's global scope in a convenient way.

@@ -27,10 +25,10 @@ ---

```
This will make the functions ok, fail and withResult, as well as the types Ok, Fail and Result globally available
This will make the functions ResultIs.success, ResultIs.failure and withResult, as well as the types Success, Failure and Result globally available
---
## ok and fail - Basic Usage
Creating `Ok` and `Fail` result objects
## ResultIs.success and ResultIs.failure - Basic Usage
Creating `Success` and `Failure` result objects
```ts
const { data, error } = ok("Hello");
const { data, error } = ResultIs.success("Hello");
if (error) {

@@ -43,8 +41,10 @@ // do something with error

or
```ts
const { data, error } = fail(new Error("Error"));
const {data, error} = ResultIs.failure(new Error("Error"));
if (error) {
// do something with error
// do something with error
} else {
// do something with data
// do something with data
}

@@ -54,10 +54,11 @@ ```

Wrapping the returns from functions with `fail` for errors, and `ok` for non-error so that the function calling it receives a `Result` type.
Wrapping the returns from functions with `ResultIs.failure` for errors, and `ResultIs.success` for non-error so that the function calling it receives a `Result` type.
```ts
// Specifying the return type here is optional, as it will be inferred without it
const fnWithResult = (): Result<string, Error> => {
if ("" !== "") {
return ok("hello");
return ResultIs.success("hello");
}
return fail(new Error("Method failed"));
return ResultIs.failure(new Error("Method failed"));
};

@@ -79,5 +80,5 @@

if ("" !== "") {
return ok("hello");
return ResultIs.success("hello");
}
return fail(new Error("Method failed"));
return ResultIs.failure(new Error("Method failed"));
};

@@ -88,5 +89,5 @@

if (error) {
return fail(error);
return ResultIs.failure(error);
}
return ok(data);
return ResultIs.success(data);
};

@@ -106,5 +107,5 @@

if ("" !== "") {
return ok("hello");
return ResultIs.success("hello");
}
return fail(new Error("Method failed"));
return ResultIs.failure(new Error("Method failed"));
};

@@ -121,5 +122,5 @@

if (error) {
return fail(error);
return ResultIs.failure(error);
}
return ok(data);
return ResultIs.success(data);
};

@@ -133,4 +134,4 @@ ```

withResult is a function which wraps another function and returns an `Err` result if the wrapped function throws an error,
or an `Ok` result if the wrapped function does not.
withResult is a function which wraps another function and returns a `Failure` result if the wrapped function throws an error,
or a `Success` result if the wrapped function does not.
```ts

@@ -150,49 +151,26 @@ import somePkg from "package-that-throws-errors";

## Error stack
One advantage of using errors as values is that we know exactly where an error was created and by whom. Taking advantage of this is still a WiP, however currently we keep track of information about all errors which we encountered in the current chain of Fail results.
```ts
const fn1 = () => fail(new Error("Hello"));
const fn2 = () => fn1().mapErr(() => new Error("World"));
const { errorStack } = fn2();
```
would produce
```json
[
{ "message": "Hello", "stack": "..." },
{ "message": "World", "stack": "..." }
]
```
---
## API
```typescript
type None = null;
type Fail<T, E extends Error = Error> = {
data: never,
export type Failure<E extends Error = Error> = {
data: null,
error: E,
unwrap(): void, // Returns the value, but throws an error if the result is an Error
unwrapOr<D>(defaultValue: D): D, // Returns the value or gives you a default value if it's an error
mapErr<E2 extends Error>(fn: (err: E) => E2): Err<T, E2>, // If the result is an error, map the error to another error
andThen<N>(fn: (data: never) => N): Err<T, E> // If the result is not an error, map the data in it
successOrThrow(): void, // Returns the value, but throws an error if the result is an Error
successOrDefault<D>(defaultValue: D): D, // Returns the value or gives you a default value if it's an error
transformOnFailure<E2 extends Error>(fn: (fail: E) => E2): Failure<E2>, // If the result is an error, map the error to another error
transformOnSuccess<N>(fn: (data: never) => N): Failure<E> // If the result is not an error, map the data in it
};
type Ok<T> = {
export type Success<T> = {
data: T,
error: never,
unwrap(): T, // Returns the value, but throws an error if the result is an Error
unwrapOr<D>(defaultValue: D): T, // Returns the value or gives you a default value if it's an error
mapErr<E2 extends Error>(fn: (err: never) => E2): Ok<T>, // If the result is an error, map the error to another error
andThen<N>(fn: (data: T) => N): Ok<N> // If the result is not an error, map the data in it
error: null,
successOrThrow(): T, // Returns the value, but throws an error if the result is an Error
successOrDefault<D>(defaultValue: D): T, // Returns the value or gives you a default value if it's an error
transformOnFailure<E2 extends Error>(fn: (fail: never) => E2): Success<T>, // If the result is an error, map the error to another error
transformOnSuccess<N>(fn: (data: T) => N): Success<N> // If the result is not an error, map the data in it
};
type Result<T, E extends Error = ErrorResult> =
| Fail<T, E>
| Ok<T>;
export type Result<
T, E extends Error = Error
> = Failure<E> | Success<T>;

@@ -202,4 +180,6 @@ ```

```ts
function fail<E extends Error>(error: E): Fail<E>;
function ok<T>(data: T): Ok<T>;
export type ResultIs = {
success<T>(data: T): Success<T>,
failure<E extends Error>(failure: E): Failure<E>
};
```

@@ -206,0 +186,0 @@

type Ok<T> = import(".").Ok<T>;
type Fail<E extends Error> = import(".").Fail<E>;
type Success<T> = import(".").Success<T>;
type Failure<E extends Error> = import(".").Failure<E>;
type Result<T, E extends Error> = import(".").Result<T, E>;
declare class ResultIs {
static success: <T>(data: T) => Success<T>;
static failure: <E extends Error>(failure: E) => Failure<E>;
}
declare function ok<T>(data: T): Ok<T>;
declare function fail<E extends Error>(error: E): Fail<E>
declare function withResult<T, E extends Error, R>(

@@ -12,2 +14,2 @@ fn: (...args: T[]) => R

...args: T[]
) => R extends Promise<infer u> ? Promise<Result<u, E>> : Result<R, E>
) => R extends Promise<infer u> ? Promise<Result<u, E>> : Result<R, E>;

@@ -1,12 +0,10 @@

import { ok, fail } from ".";
import { ResultIs } from ".";
import { withResult } from "./with-result";
if (typeof window !== "undefined") {
(window as any).ok = ok;
(window as any).fail = fail;
(window as any).ResultIs = ResultIs;
(window as any).withResult = withResult;
} else {
(globalThis as any).ok = ok;
(globalThis as any).fail = fail;
(globalThis as any).ResultIs = ResultIs;
(globalThis as any).withResult = withResult;
}
export type Fail<E extends Error = Error> = {
export type Failure<E extends Error = Error> = {
data: null,
error: E,
unwrap(): void, // Returns the value, but throws an error if the result is an Error
unwrapOr<D>(defaultValue: D): D, // Returns the value or gives you a default value if it's an error
mapFail<E2 extends Error>(fn: (fail: E) => E2): Fail<E2>, // If the result is an error, map the error to another error
andThen<N>(fn: (data: never) => N): Fail<E> // If the result is not an error, map the data in it
successOrThrow(): void, // Returns the value, but throws an error if the result is an Error
successOrDefault<D>(defaultValue: D): D, // Returns the value or gives you a default value if it's an error
transformOnFailure<E2 extends Error>(fn: (fail: E) => E2): Failure<E2>, // If the result is an error, map the error to another error
transformOnSuccess<N>(fn: (data: never) => N): Failure<E> // If the result is not an error, map the data in it
};
export type Ok<T> = {
export type Success<T> = {
data: T,
error: null,
unwrap(): T, // Returns the value, but throws an error if the result is an Error
unwrapOr<D>(defaultValue: D): T, // Returns the value or gives you a default value if it's an error
mapFail<E2 extends Error>(fn: (fail: never) => E2): Ok<T>, // If the result is an error, map the error to another error
andThen<N>(fn: (data: T) => N): Ok<N> // If the result is not an error, map the data in it
successOrThrow(): T, // Returns the value, but throws an error if the result is an Error
successOrDefault<D>(defaultValue: D): T, // Returns the value or gives you a default value if it's an error
transformOnFailure<E2 extends Error>(fn: (fail: never) => E2): Success<T>, // If the result is an error, map the error to another error
transformOnSuccess<N>(fn: (data: T) => N): Success<N> // If the result is not an error, map the data in it
};

@@ -23,43 +23,50 @@

T, E extends Error = Error
> = Fail<E> | Ok<T>;
> = Failure<E> | Success<T>;
export const fail = <E extends Error>(
const failure = <E extends Error>(
error: E
): Fail<E> => {
return {
data: null,
error: error,
unwrap() {
throw error;
},
mapFail<E2 extends Error>(fn: (err: E) => E2): Fail<E2> {
return fail<E2>(fn(error));
},
unwrapOr<D>(defaultValue: D): D {
return defaultValue;
},
andThen(): Fail<E> {
return this;
}
};
};
export const ok = <T>(
): Failure<E> => ({
data: null,
error: error,
successOrThrow() {
throw error;
},
successOrDefault<D>(defaultValue: D): D {
return defaultValue;
},
transformOnFailure<E2 extends Error>(fn: (err: E) => E2): Failure<E2> {
return failure<E2>(fn(error));
},
transformOnSuccess(): Failure<E> {
return this;
}
});
const success = <T>(
data: T
): Ok<T> => ({
): Success<T> => ({
data,
error: null,
unwrap(): T {
successOrThrow(): T {
return data;
},
mapFail(): Ok<T> {
successOrDefault(): T {
return data;
},
transformOnFailure(): Success<T> {
return this;
},
unwrapOr(): T {
return data;
},
andThen<N>(fn: (data: T) => N): Ok<N> {
return ok(fn(data));
transformOnSuccess<N>(fn: (data: T) => N): Success<N> {
return success(fn(data));
}
});
export type ResultIs = {
success<T>(data: T): Success<T>,
failure<E extends Error>(failure: E): Failure<E>
};
export const ResultIs: ResultIs = {
success,
failure
};

@@ -1,2 +0,2 @@

import { fail, Fail, Result, Ok, ok } from "../src";
import { ResultIs, Failure, Result, Success } from "../src";

@@ -11,11 +11,11 @@

beforeEach(() => {
errorInstance = fail(testError);
errorInstance = ResultIs.failure(testError);
});
it("should throw error on unwrap for Err type", () => {
expect(() => errorInstance.unwrap()).toThrow(testError);
expect(() => errorInstance.successOrThrow()).toThrow(testError);
});
it("should return default value on unwrapOr for Err type", () => {
expect(errorInstance.unwrapOr("default")).toBe("default");
expect(errorInstance.successOrDefault("default")).toBe("default");
});

@@ -25,3 +25,3 @@

const newError = new Error("New error");
const { error } = errorInstance.mapFail(() => newError);
const { error } = errorInstance.transformOnFailure(() => newError);
if (error) {

@@ -33,3 +33,3 @@ expect(error.message).toBe(newError.message);

it("should return itself on andThen for Err type", () => {
const result = errorInstance.andThen(() => "new value");
const result = errorInstance.transformOnSuccess(() => "new value");
expect(JSON.stringify(result)).toEqual(JSON.stringify(errorInstance));

@@ -40,7 +40,7 @@ });

describe("Ok type", () => {
let okInstance: Ok<string>;
let okInstance: Success<string>;
const testData = "test data";
beforeEach(() => {
okInstance = ok(testData);
okInstance = ResultIs.success(testData);
});

@@ -57,11 +57,11 @@

it("should return data on unwrap for Ok type", () => {
expect(okInstance.unwrap()).toBe(testData);
expect(okInstance.successOrThrow()).toBe(testData);
});
it("should return data on unwrapOr for Ok type", () => {
expect(okInstance.unwrapOr("default")).toBe(testData);
expect(okInstance.successOrDefault("default")).toBe(testData);
});
it("should return itself on mapErr for Ok type", () => {
const result = okInstance.mapFail(() => new Error("New error"));
const result = okInstance.transformOnFailure(() => new Error("New error"));
expect(JSON.stringify(result)).toEqual(JSON.stringify(okInstance));

@@ -72,4 +72,4 @@ });

const newValue = "new value";
const result = okInstance.andThen(() => newValue);
expect(JSON.stringify(result)).toEqual(JSON.stringify(ok(newValue)));
const result = okInstance.transformOnSuccess(() => newValue);
expect(JSON.stringify(result)).toEqual(JSON.stringify(ResultIs.success(newValue)));
expect(result.data).toBe(newValue);

@@ -76,0 +76,0 @@ });

@@ -1,2 +0,2 @@

import { fail, ok, Result, Ok, Fail } from "./index";
import { ResultIs, Result, Success, Failure } from "./index";

@@ -17,9 +17,9 @@ export const isPromise = <T>(value: T | Promise<T>): value is Promise<T> =>

return data
.then(value => ok(value) as Ok<typeof value>)
.catch(e => fail(e) as Fail<E>) as (R extends Promise<infer u> ? Promise<Result<u, E>> : Result<R, E>);
.then(value => ResultIs.success(value) as Success<typeof value>)
.catch(e => ResultIs.failure(e) as Failure<E>) as (R extends Promise<infer u> ? Promise<Result<u, E>> : Result<R, E>);
}
return ok(data) as any;
return ResultIs.success(data) as any;
} catch (error) {
const e = error instanceof Error ? error : new Error("Unknown error");
return fail(e) as any;
return ResultIs.failure(e) as any;
}

@@ -26,0 +26,0 @@ };

// Assuming a jest-like syntax for demonstration purposes.
import { ok, Result } from "../src";
import { ResultIs, Result } from "../src";
import { withResult, isPromise } from "./with-result";

@@ -23,3 +23,3 @@

const wrapped = withResult(func);
expect(JSON.stringify(wrapped(1))).toStrictEqual(JSON.stringify(ok(2)));
expect(JSON.stringify(wrapped(1))).toStrictEqual(JSON.stringify(ResultIs.success(2)));
});

@@ -42,3 +42,3 @@

const result = await wrapped(1);
expect(JSON.stringify(result)).toEqual(JSON.stringify(ok(2)));
expect(JSON.stringify(result)).toEqual(JSON.stringify(ResultIs.success(2)));
});

@@ -45,0 +45,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc