Skip to content

Commit

Permalink
add unwrapunchecked
Browse files Browse the repository at this point in the history
  • Loading branch information
bkiac committed Dec 1, 2024
1 parent 0cf5bde commit 77c111f
Show file tree
Hide file tree
Showing 10 changed files with 102 additions and 76 deletions.
2 changes: 1 addition & 1 deletion benchmarks/tryblock.bench.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const af = asyncFn(async () => {
if (one.isErr()) {
return one;
}
return Ok(one.unwrap() + 1);
return Ok(one.unwrapUnchecked() + 1);
});

function tba() {
Expand Down
4 changes: 2 additions & 2 deletions example/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,11 @@ const getAverageGrade = asyncFn(async (studentId: string) => {
});

if (grades.isErr()) {
return Err(grades.unwrapErr());
return Err(grades.unwrapErrUnchecked());
}

// Safe to unwrap because we checked for error
const value = grades.unwrap();
const value = grades.unwrapUnchecked();
return divide(
value.reduce((a, b) => a + b, 0),
value.length,
Expand Down
4 changes: 2 additions & 2 deletions src/async_result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -753,7 +753,7 @@ export class AsyncResult<T, E> implements PromiseLike<Result<T, E>> {
* ```
*/
public async unwrap(): Promise<T | null> {
return (await this).unwrap();
return (await this).unwrapUnchecked();
}

/**
Expand Down Expand Up @@ -785,7 +785,7 @@ export class AsyncResult<T, E> implements PromiseLike<Result<T, E>> {
* ```
*/
public async unwrapErr(): Promise<E | null> {
return (await this).unwrapErr();
return (await this).unwrapErrUnchecked();
}

/**
Expand Down
41 changes: 29 additions & 12 deletions src/result.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ export type ResultMatchAsync<T, E, A, B> = {
};

export class ResultImpl<T, E> {
private readonly _ok: boolean;
private readonly _value: T | E;
protected readonly _ok: boolean;
protected readonly _value: T | E;

public constructor(ok: boolean, value: T | E) {
this._ok = ok;
Expand Down Expand Up @@ -660,7 +660,7 @@ export class ResultImpl<T, E> {
* }
* ```
*/
public unwrap(): T | null {
public unwrapUnchecked(): T | null {
if (this._ok) {
return this._value as T;
}
Expand Down Expand Up @@ -706,20 +706,20 @@ export class ResultImpl<T, E> {
* @example
* ```typescript
* const x: Result<number, string> = Ok(2);
* assertEquals(x.unwrapErr(), null);
* assertEquals(x.unwrapErrUnchecked(), null);
*
* const y: Result<number, string> = Err("emergency failure");
* assertEquals(y.unwrapErr(), "emergency failure");
* assertEquals(y.unwrapErrUnchecked(), "emergency failure");
*
* const z = Result.fromThrowable(...); // Result<T, E>
* if (z.isErr()) {
* const e = z.unwrapErr() // `e` has type `E`
* const e = z.unwrapErrUnchecked() // `e` has type `E`
* } else {
* const u = z.unwrapErr() // `u` has type `null`
* }
* ```
*/
public unwrapErr(): E | null {
public unwrapErrUnchecked(): E | null {
if (!this._ok) {
return this._value as E;
}
Expand Down Expand Up @@ -1062,7 +1062,7 @@ export interface Ok<T, E = never> extends ResultImpl<T, E> {
[symbols.tag]: "Ok";

unwrap(): T;
unwrapErr(): null;
unwrapUnchecked(): T;
expect(message: string): T;
expectErr(message: string): never;

Expand All @@ -1078,18 +1078,30 @@ export interface Ok<T, E = never> extends ResultImpl<T, E> {
error(): undefined;
}

class OkImpl<T> extends ResultImpl<T, never> {
public unwrap(): T {
return this._value as T;
}
}

/**
* Contains the success value.
*/
export function Ok<T>(value: T): Ok<T> {
return new ResultImpl<T, never>(true, value) as Ok<T>;
return new OkImpl<T>(true, value) as Ok<T>;
}

class ErrImpl<E> extends ResultImpl<never, E> {
public unwrapErr(): E {
return this._value as E;
}
}

export interface Err<E, T = never> extends ResultImpl<T, E> {
[symbols.tag]: "Err";

unwrap(): null;
unwrapErr(): E;
unwrapUnchecked(): null;
unwrapErrUnchecked(): E;
expect(message: string): never;
expectErr(message: string): E;

Expand All @@ -1113,7 +1125,9 @@ export interface Err<E, T = never> extends ResultImpl<T, E> {
* Contains the error value.
*/
export function Err<E>(error: E): Err<E> {
return new ResultImpl<never, E>(false, error) as Err<E>;
const err = new ResultImpl<never, E>(false, error) as Err<E>;
err.unwrapErrUnchecked = () => error;
return err;
}

/**
Expand Down Expand Up @@ -1242,3 +1256,6 @@ export namespace Result {
*/
export const from = fromThrowable;
}

// How to have Ok() and new Ok()
// https://www.typescriptlang.org/play/?#code/MYGwhgzhAECyCeA5ArgWwEYFMBO0DeAUNNAG5gjKYBc0AdmltgNwFHTAD2tEALtssB4dsACjIVqdBjgCU+NsR4ALAJYQAdOMrQAvKXKUWxAL6tiYACYWR9VDVuMZ96bkLFi2TD2TZa0ZWqaBpjQANRSqEbQpqYEAPRx0AAimABmKrQhYH4ZPDipYMAhymA87BwYGZgwyiGgkDCc3HwCQrjZFuzk4OggIanItIIqXAS5+YUhCCgYOADCXLz8gsLyxJkA7tBiwc6z2E5wSC5ROxJ7jjTTJ2wADtgcQjzwt5LX+yyxCdBznqXFSjq3TAvX6g2GoyavCOM0YADFwTwRn49AMhkiuNstJIHLIrsd9mtoJ5vL46Jgtu9GGdKDJPtBIDCXAtmss2ix4okAMpefyA6D3R6PF7FDjQTDcHwhDK8bJFGAQJRgTx8kIQMCoEKa5QcCwQAhUnAI9HI9SCp4i3RM-Zmh4W14c74AVXVAHNMAQoWUZTw5ZgAIxWzbW+GI5Eif0ABjp0GdEAyrugAHJNknPYsOH11CAOK6RD6-f71JZrABWGQx74AeWQPFutYgNH9pdYXugBaGmAATFbDdhjRDaCIu9GmLHEgB1FQ62vJ1Pp7iZzDZ3P55p+rvFqwR6OVxI1usNmgAZkjBCAA
8 changes: 4 additions & 4 deletions tests/async_result.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,14 +122,14 @@ describe("flatten", () => {
const inner = TestOk<number, string>(42);
const flattened = TestOkPromise<Result<number, string>, boolean>(inner).flatten();
expectTypeOf(flattened).toEqualTypeOf<AsyncResult<number, string | boolean>>();
await expect(flattened.unwrap()).resolves.toEqual(inner.unwrap());
await expect(flattened.unwrap()).resolves.toEqual(inner.unwrapUnchecked());
});

it("works with an Ok<Err> result", async () => {
const inner = TestErr<number, string>("error");
const flattened = TestOkPromise<Result<number, string>, boolean>(inner).flatten();
expectTypeOf(flattened).toEqualTypeOf<AsyncResult<number, string | boolean>>();
await expect(flattened.unwrapErr()).resolves.toEqual(inner.unwrapErr());
await expect(flattened.unwrapErr()).resolves.toEqual(inner.unwrapErrUnchecked());
});

it("works with an Err result", async () => {
Expand Down Expand Up @@ -231,7 +231,7 @@ describe("map", () => {
const result = TestErrPromise<number, Error>(error);
const result2 = result.map((value) => value * 2);
const awaitedResult = await result;
await expect(result2.unwrapErr()).resolves.toEqual(awaitedResult.unwrapErr());
await expect(result2.unwrapErr()).resolves.toEqual(awaitedResult.unwrapErrUnchecked());
});
});

Expand All @@ -246,7 +246,7 @@ describe("mapAsync", () => {
const a = TestErrPromise<number, string>("error");
const b = a.map((value) => value * 2);
const awaitedResult = await a;
await expect(b.unwrapErr()).resolves.toEqual(awaitedResult.unwrapErr());
await expect(b.unwrapErr()).resolves.toEqual(awaitedResult.unwrapErrUnchecked());
});
});

Expand Down
8 changes: 4 additions & 4 deletions tests/fn.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,13 @@ describe("fn", () => {
it("returns Ok result when provided function does not throw", () => {
const wrappedFn = fn(() => Ok(42));
const result = wrappedFn();
expect(result.unwrap()).toEqual(42);
expect(result.unwrapUnchecked()).toEqual(42);
});

it("returns Err result when provided function returns Err", () => {
const wrappedFn = fn(() => Err("rekt"));
const result = wrappedFn();
expect(result.unwrapErr()).toEqual("rekt");
expect(result.unwrapErrUnchecked()).toEqual("rekt");
});

describe("types", () => {
Expand Down Expand Up @@ -143,13 +143,13 @@ describe("asyncFn", () => {
it("returns Ok result when provided async function does not throw", async () => {
const wrappedFn = asyncFn(async () => Promise.resolve(Ok(42)));
const result = await wrappedFn();
expect(result.unwrap()).toEqual(42);
expect(result.unwrapUnchecked()).toEqual(42);
});

it("returns Err result when provided async function returns Err", async () => {
const wrappedFn = asyncFn(async () => Promise.resolve(Err("rekt")));
const result = await wrappedFn();
expect(result.unwrapErr()).toEqual("rekt");
expect(result.unwrapErrUnchecked()).toEqual("rekt");
});

describe("types", () => {
Expand Down
8 changes: 4 additions & 4 deletions tests/option.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,24 +61,24 @@ describe("core", () => {
describe("okOr", () => {
it("returns the value when called on a Some option", () => {
const option = TestSome(42);
expect(option.okOr("error").unwrap()).toEqual(42);
expect(option.okOr("error").unwrapUnchecked()).toEqual(42);
});

it("returns the error value when called on a None option", () => {
const option = TestNone<string>();
expect(option.okOr("error").unwrapErr()).toEqual("error");
expect(option.okOr("error").unwrapErrUnchecked()).toEqual("error");
});
});

describe("okOrElse", () => {
it("returns the value when called on a Some option", () => {
const option = TestSome(42);
expect(option.okOrElse(() => "error").unwrap()).toEqual(42);
expect(option.okOrElse(() => "error").unwrapUnchecked()).toEqual(42);
});

it("returns the error value when called on a None option", () => {
const option = TestNone<string>();
expect(option.okOrElse(() => "error").unwrapErr()).toEqual("error");
expect(option.okOrElse(() => "error").unwrapErrUnchecked()).toEqual("error");
});
});

Expand Down
Loading

0 comments on commit 77c111f

Please sign in to comment.