Skip to content
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
20 changes: 20 additions & 0 deletions .github/workflows/npm-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: NPM publish package

on:
release:
types: [created]

jobs:
publish-npm:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
registry-url: https://registry.npmjs.org/
- run: npm ci
- run: npm run build
- run: npm run publish-npm -- --provenance
env:
NODE_AUTH_TOKEN: ${{secrets.npm_token}}
22 changes: 22 additions & 0 deletions .github/workflows/quality-check.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: Quality check

on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- run: npm run compile
- run: npm run lint
- run: npm run test
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
{
"name": "@arnonym/result",
"version": "1.0.2",
"version": "1.0.3",
"description": "Result type for TypeScript inspired by Rust",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"scripts": {
"build": "rm -rf dist; npm run check && vite build --mode esm && vite build --mode cjs",
"publish-npm": "npm run build && npm publish --access public",
"build": "rm -rf dist; vite build --mode esm && vite build --mode cjs",
"publish-npm": "npm publish --access public",
"compile": "tsc --noEmit",
"test": "jest",
"lint": "eslint",
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export { Result, Match, Ok, Err } from './result';
export { Result, type Match, type Ok, type Err } from './result';
export { pipe } from './pipe';
40 changes: 37 additions & 3 deletions src/result.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,41 @@ describe('result', () => {
const val = Result.err(-1);
expect(() => {
val.unwrap();
}).toThrow();
}).toThrow("-1");
});
});

describe('expect', () => {
test('should "expect" ok', () => {
const val = Result.ok(3);
const val2 = val.expect("You did it wrong!");
expect(val2).toBe(3);
});
test('should not "expect" err but throw with string', () => {
const val = Result.err(-1);
expect(() => {
val.expect("You did it wrong!");
}).toThrow(new Error("You did it wrong!"));
});
test('should not "expect" err but throw with function', () => {
const val = Result.err(-1);
expect(() => {
val.expect(e => `You did it wrong: ${e}`);
}).toThrow(new Error("You did it wrong: -1"));
});
});

describe('unwrapErr', () => {
test('should "unwrapErr" err', () => {
const val = Result.err(3);
const val2 = val.unwrapErr();
expect(val2).toBe(3);
});
test('should not "unwrapErr" err but throw', () => {
const val = Result.ok(-2);
expect(() => {
val.unwrapErr();
}).toThrow("Tried to unwrapErr on value: -2");
});
});

Expand Down Expand Up @@ -441,7 +475,7 @@ describe('result', () => {
});
test('assertOk on error should throw', () => {
const result = Result.err(3);
expect(() => Result.assertOk(result)).toThrow();
expect(() => Result.assertOk(result)).toThrow("Expected Ok, got Err: 3");
});
test('assertErr on err', () => {
const result = Result.err(3);
Expand All @@ -450,7 +484,7 @@ describe('result', () => {
});
test('assertOk on error should throw', () => {
const result = Result.ok(3);
expect(() => Result.assertErr(result)).toThrow();
expect(() => Result.assertErr(result)).toThrow("Expected Err, got Ok: 3");
});
});

Expand Down
31 changes: 30 additions & 1 deletion src/result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,17 @@ type IncludesType<T extends Array<unknown>, S> = {

export type NeverIfExistingInUnion<T extends Array<unknown>> = IncludesType<T, never> extends true ? never : T;

type ExpectMessage<E> = string | ((err: E) => string);

interface Functions<O, E> {
isOk: () => this is Ok<O>;
isErr: () => this is Err<E>;
map: <NO>(fn: (value: O) => NO) => Result<NO, E>;
andThen: <NO, NE>(fn: (value: O) => Result<NO, NE>) => Result<NO, E | NE>;
mapErr: <NE>(fn: (err: E) => NE) => Result<O, NE>;
unwrap: () => O;
unwrapErr: () => E;
expect: (message: ExpectMessage<E>) => O;
unwrapOr: <R1>(def: R1) => O | R1;
unwrapOrElse: <R1>(def: (err: E) => R1) => O | R1;
or: <R1>(def: R1) => Result<O | R1, never>;
Expand Down Expand Up @@ -81,7 +85,24 @@ function unwrap<O, E>(data: Result<O, E>): O {
if (data.isOk()) {
return data.value;
}
throw new Error(`Tried to unwrap error: ${data.err}`);
throw data.err;
}

function unwrapErr<O, E>(data: Result<O, E>): E {
if (data.isErr()) {
return data.err;
}
throw new Error(`Tried to unwrapErr on value: ${data.value}`);
}

function expect<O, E>(message: ExpectMessage<E>): (data: Result<O, E>) => O {
return data => {
if (data.isOk()) {
return data.value;
}
const m = typeof message === 'function' ? message(data.err) : message;
throw new Error(m);
}
}

function unwrapOr<R1>(def: R1): <O, E>(data: Result<O, E>) => O | R1 {
Expand Down Expand Up @@ -139,6 +160,14 @@ class InternalResult<O, E> implements Functions<O, E> {
return unwrap<O, E>(this as unknown as Result<O, E>);
}

unwrapErr() {
return unwrapErr<O, E>(this as unknown as Result<O, E>);
}

expect(message: ExpectMessage<E>) {
return expect<O, E>(message)(this as unknown as Result<O, E>);
}

unwrapOr<R1>(def: R1) {
return unwrapOr<R1>(def)(this as unknown as Result<O, E>);
}
Expand Down
Loading