union-type
Union types for Ruby. Combine classes into a single type that matches any of them.
Installation
gem "union-type"
Usage
Creating a union type
require "union-type"
StringOrInt = String | Integer # via Class#|
StringOrInt = UnionType[String, Integer] # bracket syntax
# => UnionType(Integer | String)
Types are stored in a SortedSet (sorted alphabetically by class name) and deduplicated. Subclasses are dropped when a superclass is already present:
UnionType[Integer, Float, Numeric]
# => UnionType(Numeric) — Integer and Float are redundant
Matching values
union = String | Integer
union === "hello" # => true
union === 42 # => true
union === :sym # => false
"hello".is_a?(union) # => true
42.kind_of?(union) # => true
"hi".instance_of?(union) # => true (exact class match)
42.instance_of?(union) # => true
42.instance_of?(Numeric | String) # => false (42 is not exactly Numeric)
case/when
case value
when String | Integer then "string or int"
when Float then "float"
end
Combining unions
# Union (|)
(String | Integer) | Float
# => UnionType(Float | Integer | String)
[String, Integer, Float].reduce(:|)
# => UnionType(Float | Integer | String)
# Intersection (&) — returns nil when empty
(String | Integer) & (Integer | Float)
# => UnionType(Integer)
(String | Integer) & Float
# => nil
Subtraction and coverage
union = String | Integer | Float
union.cover?(Integer) # => true (exact member)
union.cover?(Numeric) # => false (superclass, not covered)
numeric = Numeric | String
numeric.cover?(Integer) # => true (subclass of Numeric)
Enumerable
UnionType includes Enumerable, yielding classes in sorted order:
union = String | Integer | Float
union.to_a # => [Float, Integer, String]
union.map(&:name) # => ["Float", "Integer", "String"]
union.include?(String) # => true
union.count # => 3
union.min_by(&:name) # => Float
Opting out of core extensions
If you don't want Class#|, Object#is_a?, Object#kind_of?, or Object#instance_of? patched, require the no-ext variant instead:
require "union-type-no-ext"
union = UnionType[String, Integer] # construction still works
union === "hello" # => true
"hello".is_a?(union) # => false (Object#is_a? not patched)
API reference
| Method | Description |
|---|---|
UnionType.new(*classes) |
Create a union; raises ArgumentError if empty |
UnionType[*classes] |
Alias for .new |
| `String \ | Integer` |
union === value |
True if value matches any member |
union.cover?(klass) |
True if klass is a member or subclass of a member |
| `union \ | other` |
union & other |
Intersection; returns nil if empty |
union.types |
The underlying SortedSet |
value.is_a?(union) |
Patched is_a? (requires core ext) |
value.instance_of?(union) |
Exact class match against members (requires core ext) |
License
MIT