Skip to content

Conversation

@volf52
Copy link
Contributor

@volf52 volf52 commented Jan 6, 2026

Summary

This PR introduces a major rewrite of the core Result<T, E> and Option<T> types with significant improvements to internal architecture, type safety, async handling, and developer experience. The rewrite maintains backward compatibility while providing a more robust foundation for future development.

Stats: +12,696 additions / -8,046 deletions across 62 files


🎯 Key Changes

Core Type Rewrite

Result Type (src/result.ts)

  • New discriminant-based state tracking with _tag property for compile-time type narrowing
  • Improved context isolation - async operations now create separate contexts to prevent cross-branch mutation
  • Enhanced error type unification through Result.Err union semantics
  • New methods: mapBoth, innerMap, zipErr, flatZip, tap, tapErr
  • Better async overloads with precise type inference for sync/async chains
  • Simplified constructor pattern with Result.Ok() and Result.Err() static methods

Option Type (src/option.ts)

  • Discriminant tags ("Some" | "None") for better type narrowing
  • New constructors: fromFalsy(), fromPredicate(), fromPromise()
  • New methods: filter(), match(), all(), any(), innerMap()
  • Context isolation fixes for async chaining
  • Private #val field for better encapsulation

Custom Promise Detection

  • Replaced node:util/types with custom isPromiseLike() implementation in src/utils.ts
  • Removes Node.js-specific dependency for better cross-platform compatibility
  • More reliable Promise duck-typing for edge cases

Test Suite Overhaul

Reorganized test structure into logical subdirectories:

tests/
├── option/
│   ├── option.test.ts           # Comprehensive tests
│   ├── option-comprehensive.test.ts
│   ├── option-readme-examples.test.ts
│   └── option.async-chain.test.ts
└── result/
    ├── result.test.ts           # Comprehensive tests
    ├── result-comprehensive.test.ts
    ├── result-readme-examples.test.ts
    ├── result.async-chain.test.ts
    ├── result.flatMap.test.ts
    ├── result.flatZip.test.ts
    ├── result.map.test.ts
    ├── result.validate.test.ts
    └── result.zip.test.ts
  • Added README example tests to ensure documentation stays in sync
  • New async chain short-circuit tests for verifying async behavior
  • Migrated to Bun test runner consistently

Documentation

Added comprehensive specification documents:

  • docs/result-spec.md - Full Result type specification with design principles
  • docs/option-spec.md - Full Option type specification with design principles
  • docs/async-chain-short-circuit.md - Async behavior documentation
  • Updated README with new methods and reorganized examples

Toolchain Updates

Tool Old New
Package Manager npm/yarn Bun (primary)
Test Runner Node test runner Bun test
Linter biome oxlint + oxlint-tsgolint
Formatter biome oxfmt (with treefmt)
TypeScript 5.9.x 5.9.x (latest)
Node Target 20 22/24

CI/CD Changes

  • Migrated GitHub Actions to use Bun for faster CI runs
  • Updated Node version requirements to >=22.0.0
  • Added concurrency control (later removed due to limitations)
  • Simplified workflow configurations

Build Configuration

  • Switched to tsdown for bundling (replaced bunup)
  • Updated export paths to use .mjs/.cjs extensions explicitly
  • Added .d.mts/.d.cts type definitions for proper ESM/CJS types
  • Added treefmt.toml for unified formatting
  • Added .oxfmtrc.jsonc for formatter configuration

🔄 Breaking Changes

While the API surface remains largely compatible, there are some internal changes:

  1. Type definitions now use .d.mts/.d.cts extensions for proper module type resolution
  2. Minimum Node version bumped to 22 (from 20)
  3. Internal context objects are no longer shared across async branches (affects advanced use cases only)

✨ New API Methods

Option

Option.fromFalsy(value)           // None for falsy values
Option.fromPredicate(value, pred)  // Some if pred(value) === true
Option.fromPromise(promise)        // Wraps Promise<Option<T>>
opt.filter(predicate)              // Returns None if predicate fails
opt.match({ Some, None })          // Pattern matching
opt.innerMap(fn)                   // Map over Array<T> inside Option
Option.all(...options)             // Combine multiple Options
Option.any(...options)             // First Some or None

Result

Result.mapBoth(okFn, errFn)       // Transform both Ok and Err
res.innerMap(fn)                  // Map over Array<T> inside Result
res.zipErr(fn)                    // Zip on the Err side
res.flatZip(fn)                   // flatMap + zip combined
res.tap(fn)                       // Side effect on Ok
res.tapErr(fn)                    // Side effect on Err
Result.fromNullable(value, err)   // Ok if non-nullish
Result.fromPredicate(value, pred, err)  // Ok if pred passes
Result.tryCatch(fn, errorMapper)  // Catch sync exceptions
Result.tryAsyncCatch(fn, errorMapper)   // Catch async exceptions

📝 Migration Notes

Most users should not need to change their code. If you were relying on:

  1. Internal context sharing across async branches - this is no longer guaranteed
  2. Node 20 - upgrade to Node 22 or use Bun
  3. Type definition paths - ensure your bundler/IDE resolves .d.mts/.d.cts

🧪 Testing

All tests pass with Bun test runner:

bun test                      # Run all tests
bun test tests/result/*.test.ts  # Run Result tests only
bun run tc                    # Type-check

🔗 Related

volf52 added 30 commits October 23, 2025 02:31
Switch from pnpm to bun package manager, add oxlint configuration with
comprehensive rules, update biome settings, and restructure build
configuration. Minor code improvements include import path fix and new
flip() method in Result class.
Update development environment configurations to disable vitest
typechecking and configure Zed language servers for TypeScript
development.</think> Add vitest typecheck config and Zed editor settings

Update development environment configurations to disable vitest
typechecking and configure Zed language servers for TypeScript
development.
Migrate all test files from Node.js test runner to Bun's test framework,
replacing assert statements with expect matchers and mock functions.

Simplify HybridResult implementation by removing Phase 2+ combinators
(map, flatMap, zip, validate, etc.) while keeping core Phase 1 API
(Ok/Err constructors, unwrap, isOk/isErr, flip, toPromise, try).
Update tests to expect UnwrappedOkWithErr instead of specific error
types when unwrapping. Remove unnecessary async wrappers in expect
statements and add checks for original error types.
Add type assertions for error arrays and handle async error unwraping
properly in test assertions.</think> <think></think> Fix error handling
in hybrid result tests

Add explicit type assertions for error arrays and update async error
assertions to properly handle Promise rejections.
- Updated `either.map.test.ts` to include detailed tests for `mapRight` and `mapLeft` methods, covering synchronous and asynchronous mappers, preserving state across multiple operations, and ensuring correct behavior when chaining.
- Introduced new tests for `Either.zip` and `Either.zipRight` in `either.zip.test.ts`, validating the zipping of Right and Left values, handling of tuples, and maintaining immutability of the original Either.
- Added complex scenarios for zipping operations to ensure correct track maintenance and transformations.
@changeset-bot
Copy link

changeset-bot bot commented Jan 6, 2026

🦋 Changeset detected

Latest commit: 2e3bb9c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@carbonteq/fp Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@volf52 volf52 merged commit 1980d29 into main Jan 7, 2026
4 checks passed
@volf52 volf52 deleted the dev branch January 7, 2026 16:06
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.

2 participants