Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add match and unwrap methods to result #12

Merged
merged 3 commits into from
Sep 8, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/tricky-pandas-promise.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"ts-blocks": minor
---

Add `match` and `unwrap` methods to `result`.
38 changes: 36 additions & 2 deletions blocks/types/result.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { expect, test } from 'vitest';
import { Err, Ok, type Result } from './result';
import { assert, expect, test } from 'vitest';
import { Err, Ok, type Result, match, unwrap } from './result';

const failingFunction = <E>(err: E): Result<boolean, E> => Err(err);

Expand All @@ -18,3 +18,37 @@ test('Expect correct failed result', () => {
expect(val).toBe(null);
expect(err).toBe('I failed!');
});

test('Expect pass value from match', () => {
const res = match(
passingFunction(true),
(val) => val,
() => {
throw new Error('This should not throw');
}
);

expect(res).toBe(true);
});

test('Expect fail value from match', () => {
const res = match(
failingFunction('I failed!'),
() => {
throw new Error('This should have thrown');
},
(err) => err
);

expect(res).toBe('I failed!');
});

test('Expect pass value from unwrap', () => {
const res = unwrap(passingFunction(true));

expect(res).toBe(true);
});

test('Expect fail value from unwrap', () => {
assert.throws(() => unwrap(failingFunction('I failed!')), 'I failed!');
});
68 changes: 64 additions & 4 deletions blocks/types/result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,7 @@
*
* ## Examples
* ```ts
* const functionThatCanFail = (): Result<number, string> => {
* //...
* }
* const functionThatCanFail = (): Result<number, string> => {}
*
* const [val, err] = functionThatCanFail();
*
Expand Down Expand Up @@ -38,4 +36,66 @@ const Ok = <T>(val: T): Result<T, never> => [val, null];
*/
const Err = <E>(err: E): Result<never, E> => [null, err];

export { type Result, Ok, Err };
/** A helper method for the `Result<T, E>` type to allow you to pattern match on a Result and requires you to handle all cases.
*
* @param res The result
* @param success Function to be called if the result is `Ok`
* @param failure Function to be called if the result is `Err`
* @returns
*
* ## Examples
*
* ```ts
* const functionThatCanFail = (): Result<number, string> => {
* return Ok(10);
* };
*
* const value = match(
* functionThatCanFail(),
* (val) => val,
* (err) => {
* throw new Error(err);
* }
* );
*
* console.log(value) // 10
* ```
*/
const match = <T, V, E>(res: Result<V, E>, success: (val: V) => T, failure: (err: E) => T): T => {
const [val, err] = res;

if (err !== null) {
return failure(err);
}

// we know this matches because of the typing of Result
return success(val as V);
};

/** Tries to return the value if it can't it will throw.
*
* @param res The result
* @returns
*
* ## Examples
* ```ts
* const functionThatCanFail = () => {
* return Ok(10);
* }
*
* const value = unwrap(functionThatCanFail());
*
* console.log(value) // 10
* ```
*/
const unwrap = <T, E>(res: Result<T, E>): T => {
const [val, err] = res;

if (err !== null) {
throw new Error(JSON.stringify(err));
}

return val as T;
};

export { type Result, Ok, Err, match, unwrap };
2 changes: 1 addition & 1 deletion blocks/utilities/sleep.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { sleep } from './sleep';
test('Expect time elapsed', async () => {
const start = Date.now();

const duration = 25;
const duration = 50;

await sleep(duration);

Expand Down
5 changes: 1 addition & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,7 @@
"type": "git",
"url": "git+https://github.com/ieedan/ts-blocks"
},
"keywords": [
"changelog",
"date"
],
"keywords": ["changelog", "date"],
"author": "Aidan Bleser",
"license": "MIT",
"bugs": {
Expand Down