class T::Types::Base

def self.method_added(method_name)

def self.method_added(method_name)
  super(method_name)
  # What is now `subtype_of_single?` used to be named `subtype_of?`. Make sure people don't
  # override the wrong thing.
  #
  # NB: Outside of T::Types, we would enforce this by using `sig` and not declaring the method
  # as overridable, but doing so here would result in a dependency cycle.
  if method_name == :subtype_of? && self != T::Types::Base
    raise "`subtype_of?` should not be overridden. You probably want to override " \
          "`subtype_of_single?` instead."
  end
end

def ==(other)

`#name`) and comparing the resulting strings for equality.
Type equivalence, defined by serializing the type to a string (with
def ==(other)
  case other
  when T::Types::Base
    other.name == self.name
  else
    false
  end
end

def describe_obj(obj)

def describe_obj(obj)
  # Would be redundant to print class and value in these common cases.
  case obj
  when nil, true, false
    return "type #{obj.class}"
  end
  # In rare cases, obj.inspect may fail, or be undefined, so rescue.
  begin
    # Default inspect behavior of, eg; `#<Object:0x0...>` is ugly; just print the hash instead, which is more concise/readable.
    if obj.method(:inspect).owner == Kernel
      "type #{obj.class} with hash #{obj.hash}"
    elsif T::Configuration.include_value_in_type_errors?
      "type #{obj.class} with value #{T::Utils.string_truncate_middle(obj.inspect, 30, 30)}"
    else
      "type #{obj.class}"
    end
  rescue StandardError, SystemStackError
    "type #{obj.class} with unprintable value"
  end
end

def error_message(obj)

def error_message(obj)
ted type #{self.name}, got #{describe_obj(obj)}"

def error_message_for_obj(obj)

def error_message_for_obj(obj)
  if valid?(obj)
    nil
  else
    error_message(obj)
  end
end

def error_message_for_obj_recursive(obj)

def error_message_for_obj_recursive(obj)
  if recursively_valid?(obj)
    nil
  else
    error_message(obj)
  end
end

def hash

def hash
  name.hash
end

def name

Equality is based on name, so be sure the name reflects all relevant state when implementing.
def name
  raise NotImplementedError
end

def recursively_valid?(obj)

this will be redefined in certain subclasses
def recursively_valid?(obj)
  valid?(obj)
end

def subtype_of?(t2)

Subclasses only need to implement `subtype_of_single?`).
This method cannot be overridden (see `method_added` above).

See https://git.corp.stripe.com/stripe-internal/ruby-typer/blob/9fc8ed998c04ac0b96592ae6bb3493b8a925c5c1/core/types/subtyping.cc#L912-L950
Mirrors ruby_typer::core::Types::isSubType
def subtype_of?(t2)
  t1 = self
  if t2.is_a?(T::Private::Types::TypeAlias)
    t2 = t2.aliased_type
  end
  if t1.is_a?(T::Private::Types::TypeAlias)
    return t1.aliased_type.subtype_of?(t2)
  end
  # pairs to cover: 1  (_, _)
  #                 2  (_, And)
  #                 3  (_, Or)
  #                 4  (And, _)
  #                 5  (And, And)
  #                 6  (And, Or)
  #                 7  (Or, _)
  #                 8  (Or, And)
  #                 9  (Or, Or)
  # Note: order of cases here matters!
  if t1.is_a?(T::Types::Union) # 7, 8, 9
    # this will be incorrect if/when we have Type members
    return t1.types.all? {|t1_member| t1_member.subtype_of?(t2)}
  end
  if t2.is_a?(T::Types::Intersection) # 2, 5
    # this will be incorrect if/when we have Type members
    return t2.types.all? {|t2_member| t1.subtype_of?(t2_member)}
  end
  if t2.is_a?(T::Types::Union)
    if t1.is_a?(T::Types::Intersection) # 6
      # dropping either of parts eagerly make subtype test be too strict.
      # we have to try both cases, when we normally try only one
      return t2.types.any? {|t2_member| t1.subtype_of?(t2_member)} ||
          t1.types.any? {|t1_member| t1_member.subtype_of?(t2)}
    end
    return t2.types.any? {|t2_member| t1.subtype_of?(t2_member)} # 3
  end
  if t1.is_a?(T::Types::Intersection) # 4
    # this will be incorrect if/when we have Type members
    return t1.types.any? {|t1_member| t1_member.subtype_of?(t2)}
  end
  # 1; Start with some special cases
  if t1.is_a?(T::Private::Types::Void)
    return t2.is_a?(T::Private::Types::Void)
  end
  if t1.is_a?(T::Types::Untyped) || t2.is_a?(T::Types::Untyped)
    return true
  end
  # Rest of (1)
  subtype_of_single?(t2)
end

def subtype_of_single?(type)

Returns:
  • (T::Boolean) - This method must be implemented to return whether the subclass is a subtype
def subtype_of_single?(type)
NotImplementedError

def to_s

def to_s
  name
end

def valid?(obj)

def valid?(obj)
  raise NotImplementedError
end

def validate!(obj)

def validate!(obj)
  err = error_message_for_obj(obj)
  raise TypeError.new(err) if err
end