-
Notifications
You must be signed in to change notification settings - Fork 12
mapError method and mapCatching errorTransform parameter #8
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1198,6 +1198,147 @@ describe("Result", () => { | |
}); | ||
}); | ||
|
||
describe("mapError", () => { | ||
it("maps an encapsulated error value to a next result using a transform function", () => { | ||
const result: Result<number, CustomError> = Result.error( | ||
new CustomError("TEST_ERROR"), | ||
); | ||
const nextResult = result.mapError( | ||
(error) => new CustomError(`FROM_${error.message}`), | ||
); | ||
expectTypeOf(nextResult).toEqualTypeOf<Result<number, CustomError>>(); | ||
Result.assertError(nextResult); | ||
expect(nextResult.error).toBeInstanceOf(CustomError); | ||
expect(nextResult.error.message).toBe("FROM_TEST_ERROR"); | ||
expect(result).not.toBe(nextResult); | ||
}); | ||
|
||
it("maps an encapsulated error value to an async-result using an async transform function", async () => { | ||
const result: Result<number, CustomError> = Result.error( | ||
new CustomError("TEST_ERROR"), | ||
); | ||
const nextAsyncResult = result.mapError( | ||
async (error) => new CustomError(`FROM_${error.message}`), | ||
); | ||
expectTypeOf(nextAsyncResult).toEqualTypeOf< | ||
AsyncResult<number, CustomError> | ||
>(); | ||
expect(nextAsyncResult).toBeInstanceOf(AsyncResult); | ||
|
||
const nextResult = await nextAsyncResult; | ||
|
||
Result.assertError(nextResult); | ||
expect(nextResult.error).toBeInstanceOf(CustomError); | ||
expect(nextResult.error.message).toBe("FROM_TEST_ERROR"); | ||
}); | ||
|
||
it("lets you map over an encapsulated succes value by simply ignoring the transform function and returning the success result", () => { | ||
const result = Result.ok(2) as Result<number, CustomError>; | ||
const nextResult = result.mapError( | ||
(error) => new CustomError("TEST_ERROR", { cause: error }), | ||
); | ||
|
||
expectTypeOf(nextResult).toEqualTypeOf<Result<number, CustomError>>(); | ||
expect(result).toBe(nextResult); | ||
Result.assertOk(nextResult); | ||
expect(nextResult.value).toEqual(2); | ||
}); | ||
|
||
it("accounts for the async transform function even when it is a failed result", async () => { | ||
const result = Result.ok(2) as Result<number, CustomError>; | ||
const nextAsyncResult = result.mapError( | ||
async (error) => new CustomError("TEST_ERROR", { cause: error }), | ||
); | ||
|
||
expectTypeOf(nextAsyncResult).toEqualTypeOf< | ||
AsyncResult<number, CustomError> | ||
>(); | ||
expect(nextAsyncResult).toBeInstanceOf(AsyncResult); | ||
|
||
const nextResult = await nextAsyncResult; | ||
Result.assertOk(nextResult); | ||
expect(nextResult.value).toEqual(2); | ||
}); | ||
|
||
it("flattens a returning result from the transformation", () => { | ||
const result: Result<number, CustomError> = Result.error( | ||
new CustomError("INNER_ERROR"), | ||
); | ||
const nextResult = result.mapError((error) => | ||
Result.error(new CustomError("TEST_ERROR", { cause: error })), | ||
); | ||
expectTypeOf(nextResult).toEqualTypeOf<Result<number, CustomError>>(); | ||
Result.assertError(nextResult); | ||
expect(nextResult.error.message).toBe("TEST_ERROR"); | ||
expect(result).not.toBe(nextResult); | ||
}); | ||
|
||
it("flattens a returning result from the async transformation", async () => { | ||
const result: Result<number, CustomError> = Result.error( | ||
new CustomError("INNER_ERROR"), | ||
); | ||
const nextAsyncResult = result.mapError(async (error) => | ||
Result.error(new CustomError("TEST_ERROR", { cause: error })), | ||
); | ||
|
||
expectTypeOf(nextAsyncResult).toEqualTypeOf< | ||
AsyncResult<number, CustomError> | ||
>(); | ||
|
||
const nextResult = await nextAsyncResult; | ||
|
||
Result.assertError(nextResult); | ||
expect(nextResult.error.message).toBe("TEST_ERROR"); | ||
expect(result).not.toBe(nextResult); | ||
}); | ||
|
||
it("flattens a returning async-result from the transformation", async () => { | ||
const result: Result<number, CustomError> = Result.error( | ||
new CustomError("INNER_ERROR"), | ||
); | ||
const otherAsyncResult = Result.fromAsyncCatching( | ||
Promise.reject(new CustomError("OTHER_ERROR")), | ||
); | ||
|
||
const nextAsyncResult = result.mapError(() => otherAsyncResult); | ||
expectTypeOf(nextAsyncResult).toEqualTypeOf< | ||
AsyncResult<number, CustomError> | ||
>(); | ||
|
||
const nextResult = await nextAsyncResult; | ||
|
||
Result.assertError(nextResult); | ||
expect(nextResult.error.message).toBe("OTHER_ERROR"); | ||
expect(result).not.toBe(nextResult); | ||
}); | ||
|
||
it("does not track errors thrown inside the transformation function", () => { | ||
expect(() => | ||
Result.error(new CustomError()).mapError((_error) => { | ||
throw new CustomError("THROWN_ERROR"); | ||
}), | ||
).to.throw(CustomError); | ||
}); | ||
|
||
it("will convert a success into an async-result when an async transform function was given", async () => { | ||
const result = Result.ok(2) as Result<number, CustomError>; | ||
|
||
const asyncResult = result.mapError( | ||
async (_error) => new CustomError("TEST_ERROR"), | ||
); | ||
|
||
expectTypeOf(asyncResult).toEqualTypeOf< | ||
AsyncResult<number, CustomError> | ||
>(); | ||
|
||
expect(asyncResult).toBeInstanceOf(AsyncResult); | ||
|
||
const resolvedAsyncResult = await asyncResult; | ||
Result.assertOk(resolvedAsyncResult); | ||
expect(resolvedAsyncResult.value).toBe(2); | ||
}); | ||
}); | ||
|
||
describe("mapCatching", () => { | ||
it("does track errors thrown inside the transformation function", () => { | ||
const fn = () => | ||
|
@@ -1235,6 +1376,22 @@ describe("Result", () => { | |
Result.assertError(nextResult); | ||
expect(spy).not.toHaveBeenCalled(); | ||
}); | ||
|
||
it("transforms thrown errors when an errorTransform function is defined", async () => { | ||
const result: Result<number, CustomError> = await Result.ok( | ||
2, | ||
).mapCatching( | ||
async (): Promise<number> => { | ||
throw new Error("TEST_ERROR"); | ||
}, | ||
(_error) => { | ||
return new CustomError(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is an example of where |
||
}, | ||
); | ||
|
||
Result.assertError(result); | ||
expect(result.error).toBeInstanceOf(CustomError); | ||
}); | ||
}); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add a test for when an exception gets thrown inside the |
||
|
||
describe("recover", () => { | ||
|
@@ -1961,6 +2118,119 @@ describe("AsyncResult", () => { | |
}); | ||
}); | ||
|
||
describe("mapError", () => { | ||
it("maps an encapsulated error value to a next result using a transform function", async () => { | ||
const result: AsyncResult<number, CustomError> = AsyncResult.error( | ||
new CustomError("TEST_ERROR"), | ||
); | ||
const nextResult = result.mapError( | ||
(error) => new CustomError("MAPPED_ERROR", { cause: error }), | ||
); | ||
expectTypeOf(nextResult).toEqualTypeOf< | ||
AsyncResult<number, CustomError> | ||
>(); | ||
const resolvedResult = await nextResult; | ||
Result.assertError(resolvedResult); | ||
expect(resolvedResult.error.message).toBe("MAPPED_ERROR"); | ||
}); | ||
|
||
it("maps an encapsulated error value to an async-result using an async transform function", async () => { | ||
const result = AsyncResult.error(new CustomError()); | ||
const nextAsyncResult = result.mapError( | ||
async (error) => new CustomError("MAPPED_ERROR", { cause: error }), | ||
); | ||
expectTypeOf(nextAsyncResult).toEqualTypeOf< | ||
AsyncResult<never, CustomError> | ||
>(); | ||
expect(nextAsyncResult).toBeInstanceOf(AsyncResult); | ||
|
||
const nextResult = await nextAsyncResult; | ||
|
||
Result.assertError(nextResult); | ||
expect(nextResult.error.message).toBe("MAPPED_ERROR"); | ||
}); | ||
|
||
it("lets you map over an encapsulated success value by simply ignoring the transform function and returning the success result", async () => { | ||
const result = AsyncResult.ok(2) as AsyncResult<number, CustomError>; | ||
|
||
const spy = vi.fn(); | ||
const nextResult = result.mapError((_error) => { | ||
spy(); | ||
return new CustomError(); | ||
}); | ||
|
||
expectTypeOf(nextResult).toEqualTypeOf< | ||
AsyncResult<number, CustomError> | ||
>(); | ||
expect(spy).not.toHaveBeenCalled(); | ||
|
||
// Async result will always return a new instance | ||
expect(result).not.toBe(nextResult); | ||
|
||
const resolvedResult = await nextResult; | ||
Result.assertOk(resolvedResult); | ||
expect(resolvedResult.value).toEqual(2); | ||
}); | ||
|
||
it("flattens a returning result from the transformation", async () => { | ||
const result = AsyncResult.error(new CustomError("TEST_ERROR")); | ||
const nextResult = result.mapError((error) => | ||
Result.error(new CustomError(`FROM_${error.message}`)), | ||
); | ||
expectTypeOf(nextResult).toEqualTypeOf< | ||
AsyncResult<never, CustomError> | ||
>(); | ||
|
||
const resolvedResult = await nextResult; | ||
Result.assertError(resolvedResult); | ||
expect(resolvedResult.error.message).toBe("FROM_TEST_ERROR"); | ||
expect(result).not.toBe(nextResult); | ||
}); | ||
|
||
it("flattens a returning result from the async transformation", async () => { | ||
const result = AsyncResult.error(new CustomError("TEST_ERROR")); | ||
const nextAsyncResult = result.mapError( | ||
async (error) => new CustomError(`FROM_${error.message}`), | ||
); | ||
|
||
expectTypeOf(nextAsyncResult).toEqualTypeOf< | ||
AsyncResult<never, CustomError> | ||
>(); | ||
|
||
const nextResult = await nextAsyncResult; | ||
|
||
Result.assertError(nextResult); | ||
expect(nextResult.error.message).toBe("FROM_TEST_ERROR"); | ||
expect(result).not.toBe(nextResult); | ||
}); | ||
|
||
it("flattens a returning async-result from the transformation", async () => { | ||
const result = AsyncResult.error(new CustomError("TEST_ERROR")); | ||
const otherAsyncResult = Result.fromAsyncCatching( | ||
Promise.reject(new CustomError("OTHER_ERROR")), | ||
); | ||
|
||
const nextAsyncResult = result.mapError(() => otherAsyncResult); | ||
expectTypeOf(nextAsyncResult).toEqualTypeOf< | ||
AsyncResult<never, CustomError> | ||
>(); | ||
|
||
const nextResult = await nextAsyncResult; | ||
|
||
Result.assertError(nextResult); | ||
expect(nextResult.error.message).toBe("OTHER_ERROR"); | ||
expect(result).not.toBe(nextResult); | ||
}); | ||
|
||
it("does not track errors thrown inside the transformation function", async () => { | ||
await expect(() => | ||
AsyncResult.error(new CustomError()).mapError((): Error => { | ||
throw new CustomError(); | ||
}), | ||
).rejects.toThrow(CustomError); | ||
}); | ||
}); | ||
|
||
describe("mapCatching", () => { | ||
it("does track errors thrown inside the transformation function", async () => { | ||
const result = await AsyncResult.ok(2).mapCatching((): number => { | ||
|
@@ -1980,6 +2250,20 @@ describe("AsyncResult", () => { | |
Result.assertError(result); | ||
expect(result.error).toBeInstanceOf(CustomError); | ||
}); | ||
|
||
it("transforms thrown errors when an errorTransform function is defined", async () => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the same comments of the sync-variant of |
||
const result = await AsyncResult.ok(2).mapCatching( | ||
async (): Promise<number> => { | ||
throw new Error("TEST_ERROR"); | ||
}, | ||
(_error) => { | ||
return new CustomError(); | ||
}, | ||
); | ||
|
||
Result.assertError(result); | ||
expect(result.error).toBeInstanceOf(CustomError); | ||
}); | ||
}); | ||
|
||
describe("recover", () => { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see that you use
CustomError
a lot in the added test cases. At first glance there is nothing particularly wrong with this, although type-wise it tend to 'slip through the cracks' here and there. Are you ok with using theErrorA
andErrorB
classes to proof things like transformations of errors, etc? As bonus you only have to match against the constructor of the error.