Class: T::Types::Union

Inherits:
Base
  • Object
show all
Defined in:
lib/types/types/union.rb

Overview

Takes a list of types. Validates that an object matches at least one of the types.

Direct Known Subclasses

Private::Types::SimplePairUnion

Defined Under Namespace

Modules: Private

Instance Method Summary collapse

Methods inherited from Base

#==, #describe_obj, #error_message_for_obj, #error_message_for_obj_recursive, #hash, method_added, #subtype_of?, #to_s, #validate!

Constructor Details

#initialize(types) ⇒ Union

Don’t use Union.new directly, use ‘Private::Pool.union_of_types` inside sorbet-runtime and `T.any` elsewhere.



9
10
11
# File 'lib/types/types/union.rb', line 9

def initialize(types)
  @inner_types = types
end

Instance Method Details

#build_typeObject



40
41
42
43
# File 'lib/types/types/union.rb', line 40

def build_type
  types
  nil
end

#nameObject

overrides Base



46
47
48
49
# File 'lib/types/types/union.rb', line 46

def name
  # Use the attr_reader here so we can override it in SimplePairUnion
  type_shortcuts(types)
end

#recursively_valid?(obj) ⇒ Boolean

overrides Base

Returns:



75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/types/types/union.rb', line 75

def recursively_valid?(obj)
  member_modules = @member_modules
  if member_modules.nil?
    # Force the lazy types builder, which also computes @member_modules
    types
    member_modules = @member_modules
  end
  index = 0
  if member_modules
    # For an all-Simple union, recursively_valid? and valid? coincide
    # (Simple's recursively_valid? is exactly `obj.is_a?(raw_type)`).
    while index < member_modules.length
      return true if obj.is_a?(member_modules[index])
      index += 1
    end
  else
    members = types
    while index < members.length
      return true if members.fetch(index).recursively_valid?(obj)
      index += 1
    end
  end
  false
end

#typesObject



13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/types/types/union.rb', line 13

def types
  @types ||= begin
    flattened = @inner_types.flat_map do |type|
      type = T::Utils.coerce(type)
      if type.is_a?(Union)
        # Simplify nested unions (mostly so `name` returns a nicer value)
        type.types
      else
        type
      end
    end.uniq
    # When every member is a plain Simple (whose valid? is exactly
    # `obj.is_a?(raw_type)`), precompute the members' raw modules so
    # valid? can skip per-member dispatch. instance_of? (the is_a? only
    # narrows for static checking) so that any Simple subclass overriding
    # valid? would be excluded. Note this snapshots the members at build
    # time.
    member_modules = []
    all_simple = flattened.all? do |type|
      type.is_a?(T::Types::Simple) && type.instance_of?(T::Types::Simple) &&
        member_modules << type.raw_type
    end
    @member_modules = all_simple ? member_modules.freeze : false
    flattened
  end
end

#unwrap_nilableObject



129
130
131
132
133
134
135
136
137
138
# File 'lib/types/types/union.rb', line 129

def unwrap_nilable
  non_nil_types = types.reject { |t| t == T::Utils::Nilable::NIL_TYPE }
  return nil if types.length == non_nil_types.length
  case non_nil_types.length
  when 0 then nil
  when 1 then non_nil_types.first
  else
    T::Types::Union::Private::Pool.union_of_types(non_nil_types[0], non_nil_types[1], non_nil_types[2..-1])
  end
end

#valid?(obj) ⇒ Boolean

overrides Base

Returns:



101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/types/types/union.rb', line 101

def valid?(obj)
  member_modules = @member_modules
  if member_modules.nil?
    # Force the lazy types builder, which also computes @member_modules
    types
    member_modules = @member_modules
  end
  index = 0
  if member_modules
    while index < member_modules.length
      return true if obj.is_a?(member_modules[index])
      index += 1
    end
  else
    members = types
    while index < members.length
      return true if members.fetch(index).valid?(obj)
      index += 1
    end
  end
  false
end