Skip to content

Commit 5ad284d

Browse files
committed
feat: add unwrapOrElse
1 parent caa5b98 commit 5ad284d

File tree

2 files changed

+57
-6
lines changed

2 files changed

+57
-6
lines changed

src/lib/result.test.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,16 @@ describe("Result", () => {
2525
assert.strictEqual(result.unwrapOr("default"), "original");
2626
});
2727

28+
it("should not call unwrapOrElse when Ok", () => {
29+
const result = new Ok("value");
30+
31+
const output = result.unwrapOrElse(() => {
32+
return "fallback";
33+
});
34+
35+
assert.strictEqual(output, "value");
36+
});
37+
2838
it("should return value from expect when Ok", () => {
2939
const result = new Ok("test");
3040

@@ -39,6 +49,15 @@ describe("Result", () => {
3949
});
4050
});
4151

52+
it("should include custom message in expectErr error", () => {
53+
const result = new Ok("ok");
54+
55+
assert.throws(
56+
() => result.expectErr("expected error"),
57+
(err) => err instanceof Error && err.message.includes("expected error"),
58+
);
59+
});
60+
4261
it("should map Ok value correctly", () => {
4362
const result = new Ok(5);
4463
const mapped = result.map((x) => x * 2);
@@ -116,6 +135,16 @@ describe("Result", () => {
116135
assert.strictEqual(result.unwrapOr("default"), "default");
117136
});
118137

138+
it("should call unwrapOrElse with error when Err", () => {
139+
const result = new Err("missing");
140+
141+
const output = result.unwrapOrElse((err) => {
142+
return "fallback";
143+
});
144+
145+
assert.strictEqual(output, "fallback");
146+
});
147+
119148
it("should throw when expect is called on Err", () => {
120149
const result = new Err("test error");
121150

@@ -138,6 +167,12 @@ describe("Result", () => {
138167
assert.strictEqual(result.expectErr("should not throw"), "test error");
139168
});
140169

170+
it("should ignore message when expectErr is called on Err", () => {
171+
const result = new Err("original");
172+
173+
assert.strictEqual(result.expectErr("ignored"), "original");
174+
});
175+
141176
it("should return Err when map is called on Err", () => {
142177
const result = new Err("error") as Result<number, string>;
143178
const mapped = result.map((x) => x * 2);

src/lib/result.ts

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
// BASELINE CODE FORKED FROM https://github.com/deebloo/ts-results
22
import { toString, wait } from "./util.js";
33

4-
interface BaseResult<T, E>
5-
extends Iterable<T extends Iterable<infer U> ? U : never> {
4+
interface BaseResult<T, E> extends Iterable<
5+
T extends Iterable<infer U> ? U : never
6+
> {
67
/** `true` when the result is Ok */ readonly ok: boolean;
78
/** `true` when the result is Err */ readonly err: boolean;
89

@@ -34,6 +35,13 @@ interface BaseResult<T, E>
3435
*/
3536
unwrapOr<T2 extends T>(val: T2): T | T2;
3637

38+
/**
39+
* Returns the contained `Ok` value or computes a default from the `Err` value.
40+
*
41+
* (This is the `unwrap_or_else` in rust)
42+
*/
43+
unwrapOrElse<T2 extends T>(op: (err: E) => T2): T | T2;
44+
3745
/**
3846
* Calls `mapper` if the result is `Ok`, otherwise returns the `Err` value of self.
3947
* This function can be used for control flow based on `Result` values.
@@ -103,6 +111,10 @@ export class Err<E> implements BaseResult<never, E> {
103111
return val;
104112
}
105113

114+
unwrapOrElse<T2>(op: (err: E) => T2): T2 {
115+
return op(this.val);
116+
}
117+
106118
expect(msg: string): never {
107119
throw new Error(`${msg} - Error: ${toString(this.val)}\n${this.#stack}`);
108120
}
@@ -113,7 +125,7 @@ export class Err<E> implements BaseResult<never, E> {
113125

114126
unwrap(): never {
115127
throw new Error(
116-
`Tried to unwrap Error: ${toString(this.val)}\n${this.#stack}`
128+
`Tried to unwrap Error: ${toString(this.val)}\n${this.#stack}`,
117129
);
118130
}
119131

@@ -165,6 +177,10 @@ export class Ok<T> implements BaseResult<T, never> {
165177
return this.val;
166178
}
167179

180+
unwrapOrElse(_op: (err: never) => unknown): T {
181+
return this.val;
182+
}
183+
168184
expect(_msg: string): T {
169185
return this.val;
170186
}
@@ -225,7 +241,7 @@ export namespace Result {
225241
* @param op The operation function
226242
*/
227243
export async function wrapAsync<T, E = unknown>(
228-
op: () => Promise<T>
244+
op: () => Promise<T>,
229245
): Promise<Result<T, E>> {
230246
try {
231247
return op()
@@ -244,7 +260,7 @@ export namespace Result {
244260
*/
245261
export async function attempt<T, E>(
246262
cb: () => Promise<Result<T, E>>,
247-
{ attempts = 3, timeout = 1000, backoff = 0.5 } = {}
263+
{ attempts = 3, timeout = 1000, backoff = 0.5 } = {},
248264
) {
249265
let count = 1;
250266
let waitTime = timeout;
@@ -272,7 +288,7 @@ export namespace Result {
272288
}
273289

274290
export function isResult<T = any, E = any>(
275-
val: unknown
291+
val: unknown,
276292
): val is Result<T, E> {
277293
return val instanceof Err || val instanceof Ok;
278294
}

0 commit comments

Comments
 (0)