Module: EasyTalk::TypeIntrospection

Extended by:
T::Sig
Defined in:
lib/easy_talk/type_introspection.rb

Overview

Centralized module for robust type introspection.

This module provides predicate methods for detecting types without relying on brittle string-based checks. It uses Sorbet's type system properly and handles edge cases gracefully.

Examples:

Checking if a type is boolean

TypeIntrospection.boolean_type?(T::Boolean) # => true
TypeIntrospection.boolean_type?(TrueClass)  # => true
TypeIntrospection.boolean_type?(String)     # => false

Getting JSON Schema type

TypeIntrospection.json_schema_type(Integer) # => 'integer'
TypeIntrospection.json_schema_type(Float)   # => 'number'

Constant Summary collapse

PRIMITIVE_TO_JSON_SCHEMA =

Mapping of Ruby classes to JSON Schema types

{
  String => 'string',
  Integer => 'integer',
  Float => 'number',
  BigDecimal => 'number',
  TrueClass => 'boolean',
  FalseClass => 'boolean',
  NilClass => 'null'
}.freeze

Class Method Summary collapse

Class Method Details

.array_type?(type) ⇒ Boolean



108
109
110
111
112
# File 'lib/easy_talk/type_introspection.rb', line 108

def array_type?(type)
  return false if type.nil?

  type == Array || type.is_a?(T::Types::TypedArray) || type.is_a?(EasyTalk::Types::Tuple)
end

.boolean_type?(type) ⇒ Boolean



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/easy_talk/type_introspection.rb', line 48

def boolean_type?(type)
  return false if type.nil?
  return true if [TrueClass, FalseClass].include?(type)
  return true if type.respond_to?(:raw_type) && [TrueClass, FalseClass].include?(type.raw_type)

  # Check for T::Boolean which is a TypeAlias with name 'T::Boolean'
  return true if type.respond_to?(:name) && type.name == 'T::Boolean'

  # Check for union types containing TrueClass and FalseClass
  if type.respond_to?(:types)
    type_classes = type.types.map { |t| t.respond_to?(:raw_type) ? t.raw_type : t }
    return type_classes.sort_by(&:name) == [FalseClass, TrueClass].sort_by(&:name)
  end

  false
end

.boolean_union_type?(type_class) ⇒ Boolean



78
79
80
# File 'lib/easy_talk/type_introspection.rb', line 78

def boolean_union_type?(type_class)
  type_class.is_a?(Array) && type_class.sort_by(&:name) == [FalseClass, TrueClass].sort_by(&:name)
end

.extract_inner_type(type) ⇒ Object



202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'lib/easy_talk/type_introspection.rb', line 202

def extract_inner_type(type)
  return type if type.nil?

  if type.respond_to?(:unwrap_nilable)
    unwrapped = type.unwrap_nilable
    return unwrapped.respond_to?(:raw_type) ? unwrapped.raw_type : unwrapped
  end

  if type.respond_to?(:types)
    non_nil = type.types.find do |t|
      raw = t.respond_to?(:raw_type) ? t.raw_type : t
      raw != NilClass
    end
    return non_nil.respond_to?(:raw_type) ? non_nil.raw_type : non_nil if non_nil
  end

  type
end

.get_type_class(type) ⇒ Object



179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/easy_talk/type_introspection.rb', line 179

def get_type_class(type)
  return nil if type.nil?
  return type if type.is_a?(Class)
  return type.raw_type if type.respond_to?(:raw_type)
  return Array if type.is_a?(T::Types::TypedArray) || type.is_a?(EasyTalk::Types::Tuple)
  return [TrueClass, FalseClass] if boolean_type?(type)

  if nilable_type?(type)
    inner = extract_inner_type(type)
    return get_type_class(inner) if inner && inner != type
  end

  nil
end

.json_schema_type(type) ⇒ Object



156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/easy_talk/type_introspection.rb', line 156

def json_schema_type(type)
  return 'object' if type.nil?
  return 'boolean' if boolean_type?(type)

  resolved_class = if type.is_a?(Class)
                     type
                   elsif type.respond_to?(:raw_type)
                     type.raw_type
                   end

  PRIMITIVE_TO_JSON_SCHEMA[resolved_class] || resolved_class&.name&.downcase || 'object'
end

.nilable_type?(type) ⇒ Boolean



123
124
125
126
127
# File 'lib/easy_talk/type_introspection.rb', line 123

def nilable_type?(type)
  return false if type.nil?

  type.respond_to?(:nilable?) && type.nilable?
end

.primitive_type?(type) ⇒ Boolean



134
135
136
137
138
139
140
141
142
143
# File 'lib/easy_talk/type_introspection.rb', line 134

def primitive_type?(type)
  return false if type.nil?

  resolved = if type.is_a?(Class)
               type
             elsif type.respond_to?(:raw_type)
               type.raw_type
             end
  PRIMITIVE_TO_JSON_SCHEMA.key?(resolved)
end

.typed_array?(type) ⇒ Boolean



91
92
93
94
95
# File 'lib/easy_talk/type_introspection.rb', line 91

def typed_array?(type)
  return false if type.nil?

  type.is_a?(T::Types::TypedArray)
end