diff --git a/CHANGELOG.md b/CHANGELOG.md index dc62b52d..b26951e6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ and this project adheres to [Break Versioning](https://www.taoensso.com/break-ve ## [Unreleased] +### Added + +- Improve sum type error handling documentation (addresses #439) (@baweaver) ## [1.8.3] - 2025-06-09 diff --git a/docsite/source/combining-types/sum.html.md b/docsite/source/combining-types/sum.html.md index 4db36f57..9cfa2a45 100644 --- a/docsite/source/combining-types/sum.html.md +++ b/docsite/source/combining-types/sum.html.md @@ -18,3 +18,14 @@ nil_or_string["hello"] # => "hello" nil_or_string[123] # raises Dry::Types::ConstraintError ``` + +## Error Handling + +Sum types try each type from left to right. If all types fail, the error from the rightmost type is raised: + +``` ruby +Value = FixedAmount | Percentage + +# Raises error from Percentage (rightmost), not FixedAmount +Value.call(type: "fixed", value: -1.1) +``` diff --git a/lib/dry/types/sum.rb b/lib/dry/types/sum.rb index 7b612ca9..25ff36e9 100644 --- a/lib/dry/types/sum.rb +++ b/lib/dry/types/sum.rb @@ -4,6 +4,16 @@ module Dry module Types # Sum type # + # Sum types try each constituent type from left to right and return the result + # from the first successful type. If all types fail, the error from the rightmost + # (last attempted) type is raised, not necessarily the most "relevant" error. + # + # @example + # # Given: FixedAmount | Percentage + # # Input: { type: "fixed", value: -1.1 } + # # FixedAmount fails on value constraint, Percentage fails on type mismatch + # # Error raised will be from Percentage (rightmost), not FixedAmount + # # @api public class Sum include Composition @@ -19,6 +29,9 @@ def optional? = primitive?(nil) # # @return [Object] # + # @note Tries left type first, then right type. If both fail, raises the + # error from the right (last attempted) type for performance reasons. + # # @api private def call_unsafe(input) left.call_safe(input) { right.call_unsafe(input) }