-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
'
- Loading branch information
Showing
11 changed files
with
2,088 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
root = true | ||
|
||
[*] | ||
end_of_line = lf | ||
insert_final_newline = true | ||
|
||
[*.{js,json,yml}] | ||
charset = utf-8 | ||
indent_style = space | ||
indent_size = 2 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
/.yarn/** linguist-vendored | ||
/.yarn/releases/* binary | ||
/.yarn/plugins/**/* binary | ||
/.pnp.* binary linguist-generated |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
.yarn/* | ||
!.yarn/patches | ||
!.yarn/plugins | ||
!.yarn/releases | ||
!.yarn/sdks | ||
!.yarn/versions | ||
|
||
# Swap the comments on the following lines if you wish to use zero-installs | ||
# In that case, don't forget to run `yarn config set enableGlobalCache false`! | ||
# Documentation here: https://yarnpkg.com/features/zero-installs | ||
|
||
#!.yarn/cache | ||
.pnp.* | ||
|
||
|
||
dist |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
# resful | ||
|
||
Type safe result utilities for TypeScript. | ||
|
||
## Who should use this library? | ||
|
||
This library is intended for developer that want to void explicitly uses `Error` and `throw` in their TypeScript applications. | ||
|
||
If used in libraries is not advised to expose `Result` to the library public API. This library is thought for internal use only. | ||
|
||
## How to use | ||
|
||
### Install | ||
|
||
```sh | ||
yarn add -D resful | ||
|
||
npm install -D resful | ||
|
||
pnpm install -D resful | ||
``` | ||
|
||
### Basic usage | ||
|
||
```ts | ||
import { ok, err, isOk } from 'resful' | ||
import type { Result } from 'resful' | ||
|
||
const myFunc = (): Result<string, string> => { | ||
if (Math.random() > 0.5) { | ||
return err("bad odds") | ||
} | ||
|
||
return ok("good odds") | ||
} | ||
|
||
const res = myFunc() | ||
|
||
if (isOk(res)) { | ||
// nice stuff | ||
} | ||
``` | ||
|
||
## API | ||
|
||
### `ok` | ||
|
||
Creates an immutable (`Object.freeze`) `OkResult` object of the provided type. | ||
|
||
```ts | ||
import { ok } from 'resful' | ||
|
||
interface User { | ||
id: string | ||
email: string | ||
} | ||
|
||
const res = ok<User>({ | ||
id: '123-456', | ||
email: 'cool@email.com' | ||
}) | ||
``` | ||
|
||
### `err` | ||
|
||
Creates an immutable (`Object.freeze`) `ErrResult` object of the provided type. | ||
|
||
```ts | ||
import { err } from 'resful' | ||
|
||
const BAD_ERROR = 'error.bad' as const | ||
|
||
const res = err(BAD_ERROR) // The type of the error is inferred | ||
``` | ||
|
||
### `isOk` and `isErr` | ||
|
||
Utilities asserting if a result is either an `OkResult` or an `ErrResult`. | ||
|
||
```ts | ||
import { isOk, isErr } from 'resful' | ||
|
||
const res = /* ok(...) or err(...) */ | ||
|
||
if (isOk(res)) { | ||
// `res.ok` is accessible, res is OkResult | ||
} | ||
|
||
if (isErr(res)) { | ||
// `res.err` is accessible, res is ErrResult | ||
} | ||
``` | ||
|
||
### `unwrap` | ||
|
||
Utility to unwrap the content of a result. | ||
|
||
> NOTE: This utility will throw a `TypeError` if its input is an `ErrResult` | ||
```ts | ||
import { unwrap, ok } from 'resful' | ||
|
||
const res = ok('foobar') | ||
|
||
unwrap(res) === 'foobar' // true | ||
``` | ||
|
||
### `unwrap` | ||
|
||
Utility to unwrap the content of a result. Returning a compatible fallback value if it's an `ErrResult`. | ||
|
||
```ts | ||
import { unwrapOr, ok } from 'resful' | ||
|
||
const res = ok('foobar') | ||
|
||
unwrapOr(res, 'barbar') === 'foobar' // true | ||
``` | ||
|
||
```ts | ||
import { unwrapOr, err } from 'resful' | ||
|
||
const res = err('foobar') | ||
|
||
unwrapOr(res, 'barbar') === 'barbar' // true | ||
``` | ||
|
||
### `map` | ||
|
||
Utility to map the content of an `OkResult` into another type. | ||
|
||
> NOTE: This utility will throw a `TypeError` if its input is an `ErrResult` | ||
```ts | ||
import { map, ok } from 'resful' | ||
|
||
const res = ok('foobar') | ||
|
||
map(res, (value) => value.toUpperCase()) // 'FOOBAR' | ||
``` | ||
|
||
### `mapErr` | ||
|
||
Utility to map the content of an `ErrResult` into another type. | ||
|
||
> NOTE: This utility will throw a `TypeError` if its input is an `OkResult` | ||
```ts | ||
import { mapErr, err } from 'resful' | ||
|
||
const res = err('barbar') | ||
|
||
mapErr(res, (value) => value.toUpperCase()) // 'BARBAR' | ||
``` |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
{ | ||
"name": "resful", | ||
"type": "module", | ||
"version": "0.1.0", | ||
"description": "Type safe result utilities for TypeScript", | ||
"author": { | ||
"name": "Flavio (nickfla1) Lanternini Strippoli" | ||
}, | ||
"scripts": { | ||
"test": "vitest", | ||
"build": "tsc" | ||
}, | ||
"main": "dist/index.js", | ||
"types": "dist/index.d.ts", | ||
"files": [ | ||
"/dist" | ||
], | ||
"packageManager": "yarn@4.0.1", | ||
"devDependencies": { | ||
"typescript": "^5.2.2", | ||
"vitest": "^0.34.6" | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export * from "./result"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
import { expect, test } from "vitest"; | ||
import { ok, err, isOk, isErr, unwrap, unwrapOr, map, mapErr } from "./result"; | ||
|
||
test("creates an immutable ok result", () => { | ||
const res = ok("hello"); | ||
|
||
expect(res).toEqual({ ok: "hello" }); | ||
|
||
expect(() => { | ||
/* @ts-expect-error */ | ||
res.ok = "goodbye"; | ||
}).toThrowError(/^Cannot assign to read only property 'ok'*/i); | ||
}); | ||
|
||
test("creates an immutable error result", () => { | ||
const res = err("badcode"); | ||
|
||
expect(res).toEqual({ err: "badcode" }); | ||
|
||
expect(() => { | ||
/* @ts-expect-error */ | ||
res.err = "goodbye"; | ||
}).toThrowError(/^Cannot assign to read only property 'err'*/i); | ||
}); | ||
|
||
test("isOk returns true if result is ok", () => { | ||
expect(isOk(ok("hello"))).toBeTruthy(); | ||
expect(isOk(err("badcode"))).toBeFalsy(); | ||
}); | ||
|
||
test("isErr returns true if result is an error", () => { | ||
expect(isErr(err("badcode"))).toBeTruthy(); | ||
expect(isErr(ok("hello"))).toBeFalsy(); | ||
}); | ||
|
||
test("should unwrap a success result", () => { | ||
expect(unwrap(ok("hello"))).toStrictEqual("hello"); | ||
}); | ||
|
||
test("should throw if we try to unwrap an error", () => { | ||
expect(() => { | ||
unwrap(err("hello")); | ||
}).toThrowError(/failed to unwrap/); | ||
}); | ||
|
||
test("should return a fallback if an error is unwrapped", () => { | ||
expect(unwrapOr(err("badcode"), "hello")).toStrictEqual("hello"); | ||
}); | ||
|
||
test("it should map a result", () => { | ||
expect(map(ok("hello"), (str) => str.toUpperCase())).toStrictEqual("HELLO"); | ||
}); | ||
|
||
test("it should map an error result", () => { | ||
expect( | ||
mapErr(err("badcode"), (str) => str.replace("bad", "good")) | ||
).toStrictEqual("goodcode"); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
export type OkResult<T> = { readonly ok: T }; | ||
export type ErrResult<E> = { readonly err: E }; | ||
export type Result<T, E> = OkResult<T> | ErrResult<E>; | ||
|
||
export const ok = <T>(data: T): OkResult<T> => Object.freeze({ ok: data }); | ||
export const err = <E>(error: E): ErrResult<E> => Object.freeze({ err: error }); | ||
|
||
export const isOk = <T, E>(maybeOk: Result<T, E>): maybeOk is OkResult<T> => | ||
"ok" in maybeOk; | ||
export const isErr = <T, E>(maybeErr: Result<T, E>): maybeErr is ErrResult<E> => | ||
"err" in maybeErr; | ||
|
||
export const unwrap = <T, E>(res: Result<T, E>): T | never => { | ||
if (isErr(res)) { | ||
throw new TypeError("failed to unwrap result"); | ||
} | ||
|
||
return res.ok; | ||
}; | ||
|
||
export const unwrapOr = <T, E, O extends T>( | ||
res: Result<T, E>, | ||
fallback: O | ||
): T | O => { | ||
if (isErr(res)) { | ||
return fallback; | ||
} | ||
|
||
return res.ok; | ||
}; | ||
|
||
export type MapFn<T, R> = (item: T) => R; | ||
|
||
export const map = <T, E, R>(res: Result<T, E>, fn: MapFn<T, R>): R | never => | ||
fn(unwrap(res)); | ||
|
||
export const mapErr = <T, E, R>( | ||
res: Result<T, E>, | ||
fn: MapFn<E, R> | ||
): R | never => { | ||
if (isOk(res)) { | ||
throw new TypeError("cannot error map an ok result"); | ||
} | ||
|
||
return fn(res.err); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
{ | ||
"compilerOptions": { | ||
"module": "CommonJS", | ||
"target": "ES2020", | ||
"declaration": true, | ||
"outDir": "./dist" | ||
}, | ||
"include": [ | ||
"src/**/*" | ||
], | ||
"exclude": [ | ||
"src/**/*.test.ts" | ||
] | ||
} |
Oops, something went wrong.