Skip to content

feat(core): add Result type#17

Merged
AliiiBenn merged 3 commits intomainfrom
feat/result-type
Mar 6, 2026
Merged

feat(core): add Result type#17
AliiiBenn merged 3 commits intomainfrom
feat/result-type

Conversation

@AliiiBenn
Copy link
Member

Summary

Add Result type (Ok/Err) for simple error handling in synchronous operations.

Changes

Types

  • Result<T, E> - Union type of Ok | Err
  • Ok - Success value: { ok: true, value: T }
  • Err - Error value: { ok: false, error: E }

Constructors

ok(42)           // Ok<number> { ok: true, value: 42 }
err("error")     // Err<string> { ok: false, error: "error" }

Type Checking (2 ways)

// As functions
isOk(ok(42))    // true
isErr(err("e"))  // true

// As methods
ok(42).isOk()   // true
err("e").isErr() // true

// Type narrowing works with both
if (result.isOk()) {
  console.log(result.value)  // TypeScript knows it's Ok<T>
}

Transformations

map(ok(21), x => x * 2)           // Ok<42>
map(err("e"), x => x * 2)           // Err

flatMap(ok(5), x => ok(x * 2))    // Ok<10>
flatMap(ok(5), x => err("e"))      // Err
flatMap(err("e"), x => ok(x * 2))  // Err

mapErr(err("e"), e => new Error(e)) // Err<Error>

Extraction

getOrElse(ok(42), 0)               // 42
getOrElse(err("e"), 0)              // 0

getOrCompute(ok(42), () => heavy()) // 42 (lazy not called)
getOrCompute(err("e"), () => 42)    // 42

Pattern Matching

match(ok(42), v => v * 2, () => 0) // 84
match(err("e"), v => v * 2, () => 0) // 0

Conversions

toNullable(ok(42))  // 42
toNullable(err("e")) // null
toUndefined(ok(42))  // 42
toUndefined(err("e")) // undefined

Coverage

100% test coverage. 43 tests.

🤖 Generated with Claude Code

- Add Result<T, E> union type
- Add ok() and err() constructors
- Add isOk() and isErr() type guards with methods
- Add map(), flatMap(), mapErr() transformations
- Add getOrElse(), getOrCompute() extraction
- Add tap(), match() for side effects and pattern matching
- Add toNullable(), toUndefined() conversions
- 43 tests with 100% coverage

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Ok type - represents a successful result
* @typeParam T - The type of the value
*/
export type Ok<T> = {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Ok<T> and Err<E> types include isOk() and isErr() methods, but the Success<T> type in success.ts does not have corresponding methods. This creates an API inconsistency between Result and Outcome types. Consider adding isOk() and isErr() methods to Success<T> for consistency.

* @param fn - The side effect function
* @returns The same Result
*/
export const tap = <T, E>(result: Result<T, E>, fn: (value: T) => void): Result<T, E> => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no tapErr function to perform side effects when the Result is an Err, while there is a tap function for Ok. Consider adding this for API symmetry with map/mapErr.

@marty-action
Copy link

marty-action bot commented Mar 5, 2026

Summary

This PR adds a Result type (Ok/Err) for simple error handling in synchronous operations, complementing the existing Outcome type (Success/Cause/Exception). The implementation follows the familiar Rust-inspired Result pattern.

Critical Issues

  1. API Inconsistency between Result and Outcome (packages/core/src/result.ts:17-33, packages/core/src/success.ts:11-14): The Ok and Err types include isOk() and isErr() methods, but the Success type does not have corresponding methods. This creates an inconsistency - users of Result can use both isOk(result) function and result.isOk() method, but Outcome users only have the function form.

  2. Missing tapErr function (packages/core/src/result.ts:156-161): There is a tap function for performing side effects on Ok values, but no corresponding tapErr for Err values. This breaks symmetry with map/mapErr.

Recommendations

  1. Consider adding isOk() and isErr() methods to Success for API consistency between Result and Outcome types.

  2. Add a tapErr function for symmetry

  3. Update the README (packages/core/README.md) to document the new Maybe and Result types - currently it only documents Outcome, Success, Cause, and Exception.

Positive Notes

  • Well-structured implementation following functional programming best practices
  • Comprehensive test coverage (43 tests as mentioned)
  • Good use of TypeScript features including type guards for type narrowing
  • Objects are frozen for immutability (Object.freeze())
  • Consistent naming and API design following Rusts Result pattern
  • Exports are properly re-exported in index.ts with aliased names to avoid conflicts
  • The distinction between Result (simple errors) and Outcome (rich domain errors) provides a clear choice for developers

AliiiBenn and others added 2 commits March 6, 2026 09:40
* @param value - The success value
* @returns Ok<T>
*/
export const ok = <T>(value: T): Ok<T> =>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding an okUnit() function similar to someUnit() in the Maybe type for consistency. This would be useful for operations that succeed but don't return a meaningful value.

@AliiiBenn AliiiBenn merged commit a3d137b into main Mar 6, 2026
2 checks passed
@AliiiBenn AliiiBenn deleted the feat/result-type branch March 6, 2026 10:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant